Redis Lua脚本中64位整数比较的实现方案求助
原子更新Redis哈希中的64位时间戳(Lua 5.1兼容)
问题背景
需要将C#对象存入Redis哈希,其中ChangeEventTime字段为DateTime.Ticks(64位long型整数),要求原子条件更新:仅当传入的时间戳比Redis中存储的更新时,才执行哈希更新。当前Lua脚本因Redis内置Lua 5.1不支持64位整数,转换时会丢失精度;同时需要亚秒级精度、避免客户端脏写,且工作队列无法保证条目顺序。
可行解决方案
1. 字符串字典序比较(最优方案)
C# DateTime.Ticks是固定长度的数字字符串(最多19位),相同长度的数字字符串的字典序与数值大小完全一致,因此可以直接用字符串比较替代数值比较,完全避开Lua的64位整数限制。
修改后的Lua脚本:
local existing_time = redis.call('HGET', KEYS[1], 'ChangeEventTime') -- 若不存在旧值,或新时间戳字符串更大,则执行更新 if not existing_time or existing_time < ARGV[1] then -- 合并两次HSET为一次,提升性能 redis.call('HSET', KEYS[1], 'ChangeEventTime', ARGV[1], 'Value', ARGV[2]) end
使用说明:
- C#侧传入
ARGV[1]时,直接调用DateTime.Ticks.ToString()转为字符串即可,无需额外处理 - 该操作是原子性的,Redis会确保脚本执行期间不会有其他命令插入,完全避免脏写
- 性能最优,仅需1次HGET和最多1次HSET
2. 拆分64位整数为两个32位字段
如果必须依赖数值比较,可以将64位Ticks拆分为高32位和低32位两个字段存储,分别进行数值比较(Lua 5.1支持32位有符号整数,不会丢失精度)。
C#侧拆分逻辑:
long ticks = DateTime.UtcNow.Ticks; uint highPart = (uint)(ticks >> 32); uint lowPart = (uint)ticks; // 传入Redis时,将highPart、lowPart、Value作为参数
Lua脚本实现:
local existing_high = redis.call('HGET', KEYS[1], 'ChangeEventTimeHigh') local existing_low = redis.call('HGET', KEYS[1], 'ChangeEventTimeLow') -- 无旧值时直接写入 if not existing_high then redis.call('HSET', KEYS[1], 'ChangeEventTimeHigh', ARGV[1], 'ChangeEventTimeLow', ARGV[2], 'Value', ARGV[3]) return end -- 转换为32位整数(Lua 5.1支持范围) local existing_high_num = tonumber(existing_high) local existing_low_num = tonumber(existing_low) local new_high_num = tonumber(ARGV[1]) local new_low_num = tonumber(ARGV[2]) -- 先比较高32位,高32位相等时再比较低32位 if new_high_num > existing_high_num or (new_high_num == existing_high_num and new_low_num > existing_low_num) then redis.call('HSET', KEYS[1], 'ChangeEventTimeHigh', ARGV[1], 'ChangeEventTimeLow', ARGV[2], 'Value', ARGV[3]) end
优缺点:
- 优势:基于数值比较逻辑,直观易懂
- 劣势:增加了存储字段,C#侧需要额外的拆分/合并逻辑,复杂度略高于字符串方案
3. 避免使用的方案:Sorted Set存储
虽然Sorted Set的ZADD支持条件更新(GT参数),但Redis的Sorted Set使用双精度浮点数存储score,无法精确表示超过2^53的整数(C# Ticks最大值约9e18,远大于2^53),会导致时间戳精度丢失,因此不适合该场景。
内容的提问来源于stack exchange,提问作者Bryan Lewis
相关产品推荐
相关产品推荐

