RedisJSON 技术揭秘`JSON.NUMMULTBY` 曾经的乘法原子操作,为何被弃用?

1、指令回顾

JSON.NUMMULTBY <key> <path> <factor>
  • key:目标 Redis 键
  • path:JSONPath,默认为 $
  • factor:乘数(整数或浮点)
  • 返回值:按路径顺序返回新数字;若匹配到非数字 → null

典型示例

JSON.SET doc $ '{"b":[{"a":2},{"a":5},{"a":"c"}]}'
JSON.NUMMULTBY doc $..a 2
# => ["null", 4, 10, "null"]

2、为何被官方弃用?

原因说明
与 RFC 7396 不契合乘法并非常见“增量补丁”语义,难以与 MERGE 等指令保持一致
场景稀缺实际生产中,自乘需求远少于自增/自减
易引入浮点误差Redis 内部采用 double,多次乘法累积误差难以避免
维护成本高需要单独分支处理整型 / 浮点、溢出、NaN 等特殊情况

因此,在 RedisJSON 2.x 之后,官方鼓励开发者通过 Lua 脚本NUMINCRBY 再 SET 的方式实现乘法逻辑。

3、迁移 & 替代方案

方案一:Lua 原子运算(推荐)

local v = redis.call('JSON.GET', KEYS[1], ARGV[1])
if not v then return nil end

local num = cjson.decode(v)[1]   -- JSON.GET 单路径返回数组
if type(num) ~= 'number' then return nil end

local res = num * tonumber(ARGV[2])
redis.call('JSON.SET', KEYS[1], ARGV[1], res)
return res

调用:

EVALSHA <sha> 1 doc '$.b[0].a' 2

优点

  • 单条脚本 原子 完成读、算、写
  • 可自行处理整型/浮点边界

方案二:客户端乐观锁

  1. JSON.GET 读取旧值
  2. 计算新值 = 旧值 × factor
  3. WATCH key + JSON.SET 提交
  4. 若失败重试

缺点:存在 RTT 及重试开销,不适合高并发热点键。

方案三:离线批量乘法

对于大规模历史数据,可通过 SCAN → JSON.GET → 计算 → JSON.SET 异步跑批;期间使用双写或版本号字段保持一致性。

4、性能与精度注意事项

关注点建议
浮点误差金融/积分场景使用 整型×固定倍数 → 再除回;或使用 DECIMAL 字符串
并发冲突多客户端同时乘法 → 使用 Lua;避免读改写不一致
热点键放大若操作同一数组大量元素,考虑拆分键或分片
溢出Lua 中自行检测 num * factor 是否超出 64 位整型/双精度范围

5、示例:用 Lua 实现 2× 积分翻倍活动

-- script: double_points.lua
local path = '$.stats.points'
local val  = redis.call('JSON.GET', KEYS[1], path)
if not val then return nil end

local old = cjson.decode(val)[1]
if type(old) ~= 'number' then return nil end

local new = old * 2
redis.call('JSON.SET', KEYS[1], path, new)
return new

部署 & 调用(Python 片段):

import redis, json
r = redis.Redis()

with open('double_points.lua','r') as f:
    sha = r.script_load(f.read())

new_score = r.evalsha(sha, 1, 'user:{42}',)
print("新积分:", new_score)

6、总结

  • JSON.NUMMULTBY 已被弃用,在 2.x 以后不应再依赖。
  • 官方推荐用 Lua应用层逻辑 实现乘法原子更新。
  • 迁移时重点关注 精度并发写一致性集群哈希槽
  • 对于频繁数值操作,仍首选 JSON.NUMINCRBY;乘法仅在少数场景需要,自行封装即可。

一行结论:当你看到 NUMMULTBY,请立刻想到——“用 Lua 取代它”,让你的代码在未来的 RedisJSON 版本里依然稳如磐石。🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello.Reader

请我喝杯咖啡吧😊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值