如何在 Redis 中将列表嵌套到结构中以减少顶层?

  • 本文关键字:结构 嵌套 Redis 列表 redis
  • 更新时间 :
  • 英文 :


我想在 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 值并轻松扩展列表?

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

在某些情况下,在哈希字段中字符串化列表可能是合理的。

关于"快速逐出密钥",请确保使用UNLINK而不是DEL

如果选择字符串化,以下是使用 Lua 和 Lua CJSON 库在哈希字段中原子支持插入和删除 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"]"

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

是的,这就是我会做的方式。

但是,如果要最小化键,则别无他法,只能将对象字符串化为文本并使用 set;一旦返回字符串,也要解析字符串。

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

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

最新更新