如何在Redis中合规修改字符串的已有及新增索引?
解决Redis中ID-颜色映射与反向索引的原子更新问题
问题场景
我在Redis中用键值对存储ID对应的当前颜色:
DATA:color:ID1 = red DATA:color:ID2 = red DATA:color:ID3 = red DATA:color:ID4 = blue
同时用集合维护颜色到ID的反向索引,方便快速查询某颜色下的所有ID:
INDEX:color:red = {ID1, ID2, ID3} INDEX:color:blue = {ID4}
更新ID的颜色时,需要完成三个操作:从旧颜色的集合移除ID、加入新颜色集合、更新ID的颜色值。但核心问题是更新时不知道该ID的旧颜色,之前尝试用Lua脚本动态拼接键名,但违反了Redis的规范(脚本中禁止动态生成键,避免集群路由错误)。
合规实现方案
方案1:客户端WATCH+事务实现原子更新
这是最直接的合规方案,利用Redis的WATCH命令监控数据键,结合事务保证操作的原子性:
- 监控目标ID的数据键,防止其他客户端在事务执行前修改它:
WATCH DATA:color:ID1 - 获取该ID当前的旧颜色:
GET DATA:color:ID1 - 开启事务,依次执行三个更新操作:
MULTI SREM INDEX:color:red ID1 # 这里的red是步骤2拿到的旧颜色 SADD INDEX:color:blue ID1 SET DATA:color:ID1 blue EXEC - 如果
EXEC返回nil,说明监控的键在事务执行前被其他客户端修改,只需重试整个流程即可。
这个方案完全符合Redis规范,无需依赖Lua脚本,且能保证所有操作的原子性。
方案2:合规的Lua脚本(显式传入所有键)
如果倾向用Lua脚本保证原子性,必须把所有涉及的键都显式传入KEYS数组,不能在脚本内动态拼接键名。流程如下:
- 先在客户端获取目标ID的旧颜色:
GET DATA:color:ID1 - 编写合规的Lua脚本,所有操作的键都通过
KEYS传入:-- KEYS[1] = 目标ID的数据键(DATA:color:ID1) -- KEYS[2] = 旧颜色的索引集合键(INDEX:color:red) -- KEYS[3] = 新颜色的索引集合键(INDEX:color:blue) -- ARGV[1] = 目标ID(ID1) -- ARGV[2] = 新颜色值(blue) redis.call('SREM', KEYS[2], ARGV[1]) redis.call('SADD', KEYS[3], ARGV[1]) redis.call('SET', KEYS[1], ARGV[2]) - 调用脚本时传入所有键:
EVALSHA <SHA值> 3 DATA:color:ID1 INDEX:color:red INDEX:color:blue ID1 blue
这种方式既保证了操作的原子性,又严格遵守Redis的脚本规范,不会出现集群路由问题。
方案3:优化数据结构,用哈希表存储ID-颜色映射
可以把所有ID的颜色统一存在一个哈希表中,减少零散键的数量,管理更方便:
- 用哈希表存储ID与颜色的映射:
HSET DATA:colors ID1 red ID2 red ID3 red ID4 blue - 反向索引集合保持不变(
INDEX:color:red、INDEX:color:blue)。 - 更新流程和方案1一致,只需把
GET换成HGET,SET换成HSET即可:WATCH DATA:colors HGET DATA:colors ID1 MULTI SREM INDEX:color:red ID1 SADD INDEX:color:blue ID1 HSET DATA:colors ID1 blue EXEC
内容的提问来源于stack exchange,提问作者Jason p
相关产品推荐
相关产品推荐

