Redis:获取所有哈希值的最佳方法

时间:2016-06-30 19:21:06

标签: python database hash redis

我目前在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迭代以获取值。这太慢了,尤其是迭代。有更快的方法吗?

3 个答案:

答案 0 :(得分:6)

如果发生不体面的变化呢?转置存储数据的方式。

而不是 50k哈希,每个哈希值为5个值。每个 5个哈希值为50k值

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

现在,对于new_code,有一个哈希映射,它将包含作为成员的eventid以及它的值作为值。所以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是增量器),因此在您的情况下内存可能会增加。而且我刚刚浏览了数据,我还没有做过任何操作,所以时间也会因你的情况而有所不同。

正如您所看到的,此更改可以为您提供 5x性能提升,提高2倍内存

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

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

第一个问题:

  1. 现在你已经获得了每个哈希值中new_code的值 所有内容都在一个哈希中 - >只需一个电话。
  2. 然后你逐个更新old_code和new_code。现在,您可以使用单个调用使用hmset来完成它们。
  3. 希望这有帮助。

答案 1 :(得分: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数据集拉入本地内存,这可能会也可能不会一个问题。)

答案 2 :(得分:0)

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

有两个选项

  1. 使用管道
  2. 使用LUA
  3. 然而,这两者都是解决方法,而不是解决您的问题。要知道如何执行此操作,请在此问题中查看我的答案:Is there a command in Redis for HASH data structure similar to MGET?