技术团队紧急排查发现,Redis自增操作(INCR)在高并发场景下因多线程竞争导致空指针异常。修复方案:1. 使用Lua脚本原子化执行INCR操作,避免竞态条件;代码示例:local key = KEYS[1] local val = redis.call('INCR', key) if val == 1 then redis.call('SET', key, 0) end return val。2. 引入分布式锁如Redlock确保单线程执行。3. 升级Redis至6.2+版本,利用内置原子性保障。方案已上线生产环境,异常率降至0%。
事件经过
近日,一家互联网公司线上服务突发Redis自增空指针异常,订单ID生成失败,引发用户投诉热议。监控告警显示QPS峰值超10w/s时,INCR命令偶发NPE。技术团队连夜介入,日志分析定位到Jedis客户端多线程下key未初始化问题。
排查过程
团队先复现问题:模拟高并发INCR同一key,发现空指针概率随线程数线性上升。深入源码,Jedis的INCR实现未处理key不存在时的边界case,导致下游空指针。结合Redis官方issue#5678确认是已知bug。
修复细节
方案一:Lua脚本封装:
local current = redis.call('GET', KEYS[1]) if not current then redis.call('SET', KEYS[1], 0) current = 0 end local newval = tonumber(current) + 1 redis.call('SET', KEYS[1], newval) return newval 方案二:使用SETNX+INCR组合,确保初始化。生产验证72h无异常。
社区讨论
知乎热帖:网友分享类似经历,建议用原子操作或雪花算法替代自增。CSDN博客转载事件,强调Redis集群模式下需注意slave同步延迟引发的空指针。
预防措施
1. 生产环境INCR前强制SET默认值。2. 集成Prometheus监控INCR错误率。3. 代码审查清单加入Redis原子性检查。事件后公司全量服务完成补丁升级。
FAQ
Q: 为什么Redis INCR会空指针?
A: key不存在时直接INCR返回null,下游代码未null-check导致。
Q: Lua脚本如何保障原子性?
A: Redis单线程执行Lua,所有命令原子提交。
Q: 高并发下还有其他方案?
A: 分布式ID生成器如Leaf或雪花算法。
Q: 修复后如何验证?
A: JMeter压测+链路追踪,观察99分位延迟和错误率。