不能直接用incr生成全局唯一id,因为redis cluster中incr仅作用于单个slot,跨slot无法保证原子递增;若分片键路由到不同节点,会导致id重复,这是设计使然而非bug;必须通过hash tag(如{idgen})强制所有key落同一slot,否则lua脚本执行会因crossslot错误被服务端直接拒绝。

为什么不能直接用 INCR 生成全局唯一ID?
单机 Redis 用 INCR 没问题,但集群模式下 INCR 只作用于单个 slot,跨 slot 无法保证原子递增。如果业务分片键(如 user_id)路由到不同节点,INCR 就会生成重复 ID —— 这不是 bug,是 Redis Cluster 的设计使然。
常见错误是把 INCR 包进 Lua 脚本再 EVAL,以为能“跨节点原子”,其实不行:Lua 脚本执行前,Redis 仍要根据 key 算出所属 slot,脚本只能在那个 slot 所在节点运行,其他节点完全不参与。
所以必须让所有 ID 生成请求落到同一个 slot。办法只有一个:强制所有 key hash 到同一 slot,靠 {} 标记 hash tag:
redis-cli --eval idgen.lua , '{idgen}'
EVAL 脚本里怎么安全地生成带时间戳的 ID?
分布式 ID 通常需要时间有序、无冲突、可排序,常见方案是 Snowflake 变种。Lua 里拿不到毫秒级时间戳(redis.call('TIME') 只返回秒+微秒,且不精确),所以得靠客户端传入 current_ms 参数,由调用方保证单调递增(比如用 System.currentTimeMillis() 或 process.hrtime())。
脚本核心逻辑是:读取上一个时间戳和序列号 → 若时间相同则序列号 +1,否则重置为 0 → 更新并返回组合 ID。
关键点:
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
- 必须用
redis.call('GET', KEYS[1])读旧值,不能用redis.pcall(它捕获异常但不中断脚本,容易掩盖逻辑错误) - 序列号上限建议设为
4095(12 bit),超过就阻塞等待下一毫秒 —— Lua 里没法 sleep,所以得由客户端处理等待 - 拼接 ID 时用
string.format('%d%04d', ts, seq),避免整数溢出(Redis number 是 53-bit IEEE754,大 ID 用字符串更稳)
如何保证 Lua 脚本在集群中只运行在一个 slot?
这是最容易被忽略的硬性前提。Redis Cluster 要求脚本里所有 key 必须落在同一 slot,否则报错 CROSSSLOT Keys in request don't hash to the same slot。
解决方法只有两个:
- 所有 key 都带相同 hash tag,例如
idgen:{shard1}、seq:{shard1},其中{shard1}决定 slot - 或者只用一个 key,把时间戳和序列号存成 JSON 字符串,比如
GETSET idgen:{global} '{"ts":1717023456123,"seq":12}'
别试图用 KEYS[1] 和 KEYS[2] 分开存 —— 即使你传入 {idgen} 和 {seq},只要花括号内容不同,就可能路由到不同节点。
客户端调用时要注意哪些坑?
脚本本身没问题,但客户端一不小心就掉坑里:
- Java Jedis 要用
jedis.eval(script, Collections.singletonList("{idgen}"), Arrays.asList(currentMsStr)),注意KEYS是List,ARGV是List,顺序不能错 - Python redis-py 的
eval(script, 1, '{idgen}', current_ms)第二个参数是 key 数量,不是 key 列表,容易写错 - Node.js ioredis 支持
eval(script, ['{idgen}'], [currentMs]),但若用了 pipeline,必须确保所有命令都指向同一 slot,否则 pipeline 会失败 - 超时设置要宽松些:Lua 脚本默认最大执行时间是 5 秒(
lua-time-limit),序列号重置等待逻辑如果卡住,可能触发BUSY错误
真正麻烦的是时钟漂移 —— 如果客户端时间回拨,生成的 ID 会变小,破坏单调性。生产环境必须校准 NTP,或改用混合逻辑(比如用 Redis 的 TIME 做基准,客户端只提供 delta)。










