Redis缓存存储产品数据时并行更新的一致性问题如何解决
Redis 并行更新JSON字段避免覆盖的解决方案
方案1:使用Hash结构存储扁平数据(优先推荐)
如果你的存储内容是无嵌套的一级JSON结构,直接改用Redis Hash类型存储即可,天然支持单个字段的原子更新,完全避免全量覆盖问题:
- 写入初始数据:
HSET testKey testA A testB B testC C - request1更新字段:
HSET testKey testB Bx - request2更新字段:
HSET testKey testC Cx
两个HSET操作都是Redis原生原子操作,并行执行不会互相干扰,性能损耗可以忽略,兼容性最好,所有Redis版本都支持。
方案2:使用RedisJSON模块操作嵌套JSON
如果你的JSON有嵌套结构,推荐启用RedisJSON模块(Redis 6.0+官方支持,云厂商Redis实例大多默认开启),支持直接按JSON路径原子修改单个字段:
- 写入初始数据:
JSON.SET testKey $ '{"testA":"A","testB":"B","testC":"C"}' - request1更新字段:
JSON.SET testKey $.testB '"Bx"' - request2更新字段:
JSON.SET testKey $.testC '"Cx"'
所有JSON.SET针对单个路径的修改都是原子执行,不需要读取全量JSON再写回,性能接近原生Hash操作。
方案3:用Lua脚本实现原子读写(兼容所有Redis版本)
如果无法使用RedisJSON,又需要存储JSON字符串,可以通过Lua脚本实现修改逻辑的原子执行,Redis会保证整个Lua脚本运行期间不会插入其他请求:
示例脚本如下:
-- 入参:KEYS[1]为目标key,ARGV[1]为要修改的字段名,ARGV[2]为新值 local targetKey = KEYS[1] local updateField = ARGV[1] local updateValue = ARGV[2] -- 读取当前JSON值 local rawJson = redis.call('GET', targetKey) if not rawJson then return 0 end -- 解析修改后写回 local jsonData = cjson.decode(rawJson) jsonData[updateField] = updateValue redis.call('SET', targetKey, cjson.encode(jsonData)) return 1
调用时直接传入对应参数即可,无需额外加锁,不会出现并发覆盖问题。
方案4:WATCH乐观锁(低冲突场景适用)
如果不想调整存储结构也不想写Lua脚本,可以用Redis的WATCH机制实现乐观锁,冲突时自动重试:
while True: WATCH testKey currentValue = GET testKey newVal = 修改currentValue对应字段后的结果 MULTI SET testKey newVal result = EXEC if result is not None: // 执行成功,跳出循环 break // 执行失败说明key被其他请求修改,重试
该方案在冲突概率低于10%的场景性能优于加锁方案,冲突高的场景不推荐。
内容的提问来源于stack exchange,提问作者Sachin Shrivastava
相关产品推荐
相关产品推荐

