关注公众号【1024个为什么】,及时接收最新推送文章!
业务场景
接受某个定时任务处理流水的一个接口,使用 redis 缓存流水ID,用做幂等控制,用的数据类型是 hash,
hash 内的
key=流水ID(长这个样子625736952578072728),
value=UUID(长这个样子9def28a8-cd48-4fb6-9c93-0ac66b8450f5),
数据量约 1600W,每月增量60W,约占 3G 内存空间。
任务每月 5 号执行一次,耗时 1.5 天,hash 的过期时间是月底,到期自动删除。
问题表象
最近 2 个月,连续发生了 2 次 服务加分布式锁超时的问题,持续几秒钟,出问题的服务都是和上面的接口共用的 redis。
排查是由于 redis 主从切换,导致加锁超时。
由于上游系统逻辑中没有兼容这种场景,加锁超时影响到了业务数据,所以就需要深入排查是什么导致的 redis 主从切换。
redis 部署结构
哨兵 + 主从
问题原因
hash 的大 key 过期后自动删除,导致主线程阻塞,
哨兵检测到主节点异常,就启动了主从切换,
主节点的主线程阻塞 + 主从切换,就导致客户端的加锁请求超时。
本场景中 redis 使用不当之处
1、无效且过长的 value,hash 中的 value 如果用不到,设置一个字符即可,没必要设置那么长的,浪费内存
2、hash 上的自动过期慎用,你无法控制 hash 里的内容的数量大小,很容易造成大 key
3、自动过期的时间设置不合理,根据业务场景评估出合理的过期时间,而且不能设置同一时间
解决方案
把 hash 换成 string ?
1600W 数据,key 还是流水ID,value 按 1 个字节算,大约占用 1.6G 的空间。(可以根据 redis 对象结构估算)
空间占用是少了些,但一个业务场景就占这么多,感觉还是不太合理,而且每月数据量还会递增,还要考虑过期时间的设置。
通过数据库实现幂等
经过再次对业务场景的认真分析,1600W 数据,1.5天执行完,压力很小的,完全可以通过数据库实现幂等控制。
幸运的是,经过对业务场景的评估,发现此功能已经无用,所以就下掉了次功能。
但最后一次任务执行写入 redis 的数据到月底才会自动过期删除,我们想通过手动删除,立即释放这部分资源,缓解 redis 存储压力。
但不幸的是,又遇到了新的问题。
人工删除大 key 时的坑
人工删除的表现为:
也触发了主从切换,
而且切换后,发现主、从节点上大 key 又回来了。
具体过程为,老的主节点好不容易把大 key 删除了,
结果主从切换后,发生了主从同步,又把原来从节点上的大 key 同步过来了,
死循环了,删了个寂寞。
为什么哨兵自动主从切换没有陷入死循环?
人工删除属于立即删除,
过期删除属于惰性删除,
相比立即删除,要快一些,且主从同步不会同步过期 key 的数据。
怎么破解?
先手工断开主从关系
再进行大 key 删除
删除后再挂回主从关系
思考
看似很简单的一个业务场景,却涉及到了很多知识点。
redis 数据类型的选择,value 值的设置;
redis 对象结构,占用空间的预估;
redis 的惰性删除、立即删除;
redis 的部署结构,主从切换、主从同步;
对业务场景所耗资源的合理评估等等。
原创不易,多多关注,一键三连,感谢支持!