【问题标题】:Redis: the best way to get all hash valuesRedis:获取所有哈希值的最佳方式
【发布时间】:2016-11-03 01:13:39
【问题描述】:

我目前在我的 Redis 表中存储了大约 50k 散列,每个散列都有 5 个键/值对。我每天运行一次批处理作业来更新哈希值,包括将一些键值设置为哈希中另一个键的值。

这是我的 python 代码,它遍历键并将 old_code 设置为 new_code,如果给定哈希值存在 new_code 值:

pipe = r.pipeline()

for availability in availabilities:
    pipe.hget(availability["EventId"], "new_code")

for availability, old_code in zip(availabilities, pipe.execute()):
    if old_code:
        availability["old_code"] = old_code.decode("utf-8")

for availability in availabilities:
    if "old_code" in availability:
        pipe.hset(
            availability["EventId"], "old_code", availability["old_code"])
    pipe.hset(availability["EventId"], "new_code", availability["MsgCode"])
pipe.execute()

对我来说有点奇怪,我必须对键进行两次迭代才能达到相同的结果,有没有更好的方法来做到这一点?

我想弄清楚的另一件事是如何以最佳性能获得所有哈希值。这是我目前的做法:

d = []
pipe = r.pipeline()
keys = r.keys('*')
for key in keys:
    pipe.hgetall(key)
for val, key in zip(pipe.execute(), keys):
    e = {"event_id": key}
    e.update(val)
    if "old_key" not in e:
        e["old_key"] = None
    d.append(e)

所以基本上我会使用keys * 然后在所有键上使用HGETALL 进行迭代以获取值。这太慢了,尤其是迭代。有更快的方法吗?

【问题讨论】:

  • 您能否在不使用任何编程语言的情况下对问题的第一部分进行更多解释。
  • stackoverflow.com/questions/38065714/… 在这里查看我的答案
  • 对于每个哈希/行,我获取当前代码并将其存储为 new_code,同时将当前 new_code 保存为 old_code(以记录以前的状态并在每次数据拉取时比较 old_code 和 new_code)。
  • 你能用伪代码编写你的第一个代码吗?理解起来很模糊,也不清楚您要达到的目标。在您的第二个代码中,您为什么使用“keys *”?你在整个数据库中只有 50k 哈希吗?你的哈希值,事件ID的所有名称是什么??

标签: python database hash redis


【解决方案1】:

来个颠倒的变化怎么样。转置存储数据的方式。

而不是 50k 散列,每个散列有 5 个值。有 5 个散列,每个散列有 50k 个值

例如,您的哈希取决于 eventid,并且您将 new_code、old_code 和其他内容存储在该哈希中

现在,对于 new_code 有一个哈希映射,它将包含 eventid 作为成员,它的 value 作为 value。所以 new_code 本身就是一个包含 50k 个成员值对的哈希映射。

所以循环通过 5 而不是 50k 会相对更快。

我做了一个小实验,下面是数字

50k hashes * 5 elements 
Memory : ~12.5 MB
Time to complete loop through of elements : ~1.8 seconds

5 hashes * 50k elements
Memory : ~35 MB
Time to complete loop through of elements : ~0.3 seconds.

我已经使用 KEY_i 和 VALUE_i(其中 i 是增量器)等简单字符串进行了测试,因此在您的情况下内存可能会增加。而且我刚刚浏览了数据,我没有做任何操作,所以时间也会因您的情况而异。

如您所见,此更改可以为您带来 5 倍的性能提升,以及 2 倍的内存

Redis 对某个范围内的哈希进行压缩(512 - 默认值)。由于我们存储的范围超过了该范围(50k),因此我们在内存中出现了这个峰值。

基本上这是一种权衡,您可以选择最适合您的应用的最佳方案。

对于您的第一个问题:

  1. 您在每个散列中都获得了 new_code 的值,现在您有了 一切都在一个哈希中 -> 只需一个调用。
  2. 然后您将一一更新 old_code 和 new_code。现在,您可以使用 hmset 使用单个调用来完成它们。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    对于您的第一个问题,使用 Lua 脚本肯定会提高性能。这是未经测试的,但类似于:

    update_hash = r.register_script("""
        local key = KEYS[1]
        local new_code = ARGS[1]
    
        local old_code = redis.call("HGET", key, "new_code")
        if old_code then
            redis.call("HMSET", key, "old_code", old_code, "new_code", new_code)
        else
            redis.call("HSET", key, "new_code", new_code)
        end
    """)
    
    # You can use transaction=False here if you don't need all the 
    # hashes to be updated together as one atomic unit.
    pipe = r.pipeline()
    
    for availability in availabilities:
        keys = [availability["EventId"]]
        args = [availability["MsgCode"]]
    
        update_hash(keys=keys, args=args, client=pipe)
    
    pipe.execute()
    

    对于您的第二个问题,您可以通过编写一个简短的 Lua 脚本再次使其更快。您的脚本不会获取所有密钥并将它们返回给客户端,而是获取密钥和与它们关联的数据并在一次调用中返回。

    (但请注意,无论您在哪里调用 keys() 本质上都会很慢。请注意,在任何一种方法中,您实际上都是将整个 Redis 数据集拉入本地内存,这可能会或可能不会成为问题。)

    【讨论】:

      【解决方案3】:

      没有这样的命令,redis 哈希在哈希内工作,因此 HMGET 在一个哈希内工作,并给出该哈希中的所有字段。 无法访问多个哈希中的所有字段

      有两种选择

      1. 使用管道
      2. 使用 LUA

      但是,这两种方法都是变通方法,不能解决您的问题。要知道如何做到这一点,请查看我在这个问题中的答案:Is there a command in Redis for HASH data structure similar to MGET?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 2011-06-23
        • 2013-12-11
        • 2017-07-10
        • 1970-01-01
        • 2016-02-06
        • 1970-01-01
        相关资源
        最近更新 更多