【问题标题】:Redis: Find keys that existRedis:查找存在的键
【发布时间】:2014-01-20 23:31:46
【问题描述】:

我们有一个存储在 redis 中的数字列表作为键(3 亿键是 10 位数字键)。

我们的用户向我们提供了一个包含大约 100 万个数字的列表,并希望我们从这些数字中取出一个在 redis 中不存在的子集作为键。期望在亚秒内得到结果,我们一直在尝试使用 Redis。

最初它看起来是正确的方法(使用 EXISTS),但现在我们质疑是否有更好的方法来获得结果,而无需遍历这些数字并创建子集。

有人可以告诉我们如何有效地做到这一点吗?

【问题讨论】:

    标签: performance redis bigdata


    【解决方案1】:

    我知道的老问题,但我认为它应该得到更全面的答案。

    从 redis 获取所有密钥然后进行包含测试的问题是,每次检查都必须从 redis 中提取 300m 个密钥,或者保留这些密钥的“本地”副本,这会破坏 redis 中的要点。

    与其把数据交给处理,不如把处理交给数据。

    你可以使用redis sets,让redis做set diffing。

    这里使用python-redis,但显然redis的执行可以用任何语言完成。

    import os, base64, time, redis
    
    r = redis.Redis()
    
    def create_keys(n, size=10):
        data = base64.b64encode(os.urandom(n * size))
        return [data[i:i+size] for i in range(0, n * size, size)]
    
    if not r.exists('ref_keys'):
        for _ in range(3):
            r.sadd('ref_keys', *create_keys(1*10**6))
    print('{} keys in reference key set'.format(r.scard('ref_keys')))
    existing_keys = r.srandmember('ref_keys', number=50*10**3)
    keys_to_check = existing_keys + create_keys(50*10**3)
    start = time.time()
    try:
        r.sadd('check_ref', *keys_to_check)
        missing = r.sdiff('check_ref', 'ref_keys')
    finally:
        r.delete('check_ref')
    print('number of missing keys: {}, time taken {:0.3f}s'.format(len(missing), time.time() - start))
    

    (这里的大部分代码(和时间)都用于创建测试用例。)

    只需要传输选中的 1m 密钥,而不是全部 300m。

    注意:由于记忆,我的ref_keys 集只有 30m 个密钥,收容测试用了 3 秒。 SDIFF 具有“时间复杂度:O(N),其中 N 是所有给定集合中的元素总数。”所以我怀疑你很难在商品硬件上将时间控制在 1 秒以下。

    【讨论】:

      【解决方案2】:

      是的,您应该避免在用户列表上循环并为每个键使用 EXISTS。与常用语言中的变量操作相比,Redis 命令相对较慢(因为客户端/服务器架构)。

      我建议的一个解决方案需要一些编码:我将使用 KEYS (http://redis.io/commands/keys) 获取所有现有密钥,然后对结果和用户列表进行排序。 然后您可以实现快速搜索以检查用户的密钥是否在 redis 密钥中。

      其实你可以在 Python 中使用 set,区别已经编码 http://docs.python.org/2/library/sets.html (这是未排序的,但实现是一个字典,它是一个哈希表)。

      【讨论】:

      • KEYS 永远不应该用于生产 redis 实例,因为它的复杂度为 O(n),并且会在运行时阻塞其他请求。在 OP 的实例上运行需要一些时间,因为它有 3 亿个键。
      • 应该小心使用,我同意。对于 300M 条目来说,速度似乎也太慢了。也许应该在 Redis 之外维护一份密钥列表的副本,以便更快地访问。
      • @Pixou,仅具有 3 亿个条目的键就需要约 12 GB 的存储空间。将这些数据保存在 Redis 中并在 Redis 之外拥有密钥列表将是一件代价高昂的事情。您还可以建议您所说的“在 Redis 外部维护以便更快速地访问”是什么意思。我们可以使用什么技术/数据结构?参考可以在这里提供帮助。再次感谢您的建议。
      • 我的意思是尽量避免对用户提交的每个密钥进行一次 redis 调用,因为 redis 调用有点昂贵(TCP 套接字)。也许您应该坚持使用 redis 并尝试通过流水线提高性能(不确定这是否可行)。您还可以考虑通过 unix 套接字连接到 redis 吗?但是,可能的改进是有限的。
      • 越想越觉得难。具有优化数据结构的 Ecen,搜索需要一些时间,并且您必须执行 1M 次。我有几个想法:你能假设你收到的 1M 键是排序的(你能要求用户这样做)吗?你能有几台机器来做这项工作吗?另外一个问题:在网络方面,从用户那里获取和处理 1M 密钥确实需要一些时间(它是 40MB!)。这不是又一个大挑战吗?总的来说,我不确定这是否可以集中方式完成。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-09
      • 1970-01-01
      相关资源
      最近更新 更多