【问题标题】:How to nest a list into a structure in Redis to reduce top level?如何将列表嵌套到 Redis 中的结构中以减少顶层?
【发布时间】:2020-01-30 04:58:43
【问题描述】:

我想在redis中维护一些元数据。

meta_key = build_key()
meta_data = {
    "user": 12345,
    "tag": "D12321341234123",
    }
res = redis_sip.hmset(meta_key, meta_data)

它按预期工作。

现在我想在这个 meta_data 结构中保留一个列表,并能够向列表中添加元素。

例如:

meta_data = {
    "user": 12345,
    "tag": "D12321341234123",
    "items": []
    }

但它立即抛出异常:

redis.exceptions.DataError: Invalid input of type: 'list'. Convert to a byte, string or number first.

我认为我可以创建一个新密钥并使用zadd 来维护一个列表。但是我想尽量减少密钥的数量。这是因为一旦用户退出,我需要快速使密钥失效。将钥匙保持在最低限度可以帮助我

1) 快速驱逐密钥

2) 避免错误,因为保留标签的键较少

有什么办法可以将列表保存在 redis 值中并轻松扩展列表?

【问题讨论】:

标签: redis


【解决方案1】:

在大多数情况下,使用流水线命令的SADDZADD 会更好。如果存在另一个客户端可能会在两者之间获得密钥的风险,则使用 MULTI/EXEC 事务,从而获得不完整的对象。

在少数情况下,在哈希字段中对列表进行字符串化可能是合理的。

关于“快速驱逐密钥”,请务必使用UNLINK 而不是DEL

如果您选择字符串化,以下是如何使用 LuaLua CJSON library 在哈希字段中以原子方式支持插入和删除 JSON 编码数组:

插入

local items = cjson.decode(redis.call('HGET', KEYS[1], 'items'))
table.insert(items, ARGV[1])
return redis.call('HSET', KEYS[1], 'items', cjson.encode(items))

按值删除

local items = cjson.decode(redis.call('HGET', KEYS[1], 'items'))
local pos = -1;
for i, v in ipairs(items) do
    if ARGV[1] == v then
        pos = i
        break
    end
end
if pos == -1 then
    return -1
else
    table.remove(items, pos)
    return redis.call('HSET', KEYS[1], 'items', cjson.encode(items))
end

使用示例

> HGETALL meta_key
1) "user"
2) "12345"
3) "tag"
4) "D12321341234123"
5) "items"
6) "{}"
> EVAL "local items = cjson.decode(redis.call('HGET', KEYS[1], 'items')) \n table.insert(items, ARGV[1]) \n return redis.call('HSET', KEYS[1], 'items', cjson.encode(items))" 1 meta_key value1
(integer) 0
> HGETALL meta_key
1) "user"
2) "12345"
3) "tag"
4) "D12321341234123"
5) "items"
6) "[\"value1\"]"
> EVAL "local items = cjson.decode(redis.call('HGET', KEYS[1], 'items')) \n table.insert(items, ARGV[1]) \n return redis.call('HSET', KEYS[1], 'items', cjson.encode(items))" 1 meta_key value2
(integer) 0
> HGETALL meta_key
1) "user"
2) "12345"
3) "tag"
4) "D12321341234123"
5) "items"
6) "[\"value1\",\"value2\"]"
> EVAL "local items = cjson.decode(redis.call('HGET', KEYS[1], 'items')) \n local pos = -1; \n for i, v in ipairs(items) do \n     if ARGV[1] == v then \n     pos = i \n     break \n end \n end \n if pos == -1 then \n     return -1 \n else \n     table.remove(items, pos) \n return redis.call('HSET', KEYS[1], 'items', cjson.encode(items)) \n end" 1 meta_key value1
(integer) 0
> HGETALL meta_key
1) "user"
2) "12345"
3) "tag"
4) "D12321341234123"
5) "items"
6) "[\"value2\"]"
> EVAL "local items = cjson.decode(redis.call('HGET', KEYS[1], 'items')) \n local pos = -1; \n for i, v in ipairs(items) do \n     if ARGV[1] == v then \n     pos = i \n     break \n end \n end \n if pos == -1 then \n     return -1 \n else \n     table.remove(items, pos) \n return redis.call('HSET', KEYS[1], 'items', cjson.encode(items)) \n end" 1 meta_key value3
(integer) -1
> HGETALL meta_key
1) "user"
2) "12345"
3) "tag"
4) "D12321341234123"
5) "items"
6) "[\"value2\"]"

【讨论】:

    【解决方案2】:

    我认为我可以创建一个新密钥并使用 zadd 来维护一个列表

    是的,我就是这样做的。

    但是,如果你想最小化键,你别无他法,只能将对象字符串化为文本并使用 set;一旦你得到它,也解析字符串。

    这会限制您访问对象的单个属性的方式,但您可能已经知道了。

    如果您为每个 metaKey 选择附加列表,您可以在插入/获取/删除项目时使用管道来减少数量。往返次数。

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 2018-11-19
      • 1970-01-01
      • 2020-09-16
      • 2021-06-07
      • 2019-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多