网站建设的行业,手机应用市场下载安装到手机,中国互联网协会会长,建教育网站需要多少钱关注【离心计划】#xff0c;一起离开地球表面 背景
组内有一个系统中有一个延迟任务的需求#xff0c;关于延迟任务常见的做法有时间轮、延迟MQ还有Redis Zset等方案#xff0c;关于时间轮#xff0c;这边小苏有一个大学时候做的demo#xff1a;
https://github.com/JA…
关注【离心计划】一起离开地球表面 背景
组内有一个系统中有一个延迟任务的需求关于延迟任务常见的做法有时间轮、延迟MQ还有Redis Zset等方案关于时间轮这边小苏有一个大学时候做的demo
https://github.com/JAYqq/GoDelayTasks
该系统采用的是zset的方案在系统稳定运行了三年多后这周出现了一个大面积故障背后的原因居然是zscan的问题我们今天就简单复盘一下这次的故障好好盘一盘zset。 zset实现延时任务队列
关于zset的底层数据结构和基本操作在之前的文章就已经阐述过了简单来说就是底层由ziplist组织超过一定阈值默认128就改为由skiplist
【专栏】基础篇03| Redis 花样的数据结构
最常见的延迟任务就是下单某宝中我们下单未支付后会倒计时一段时间到点后订单自动释放还有完成订单后超过一定时间就会自动签收。这些都是延迟任务在zset中我们将业务类型作为key、订单ID作为member、下单时间延迟时间作为score这样的一个zset结构我们配合zrangeByScore(0,currentTime)就能获取到当前时间应该过期的任务了简单操作如下
127.0.0.1:6379 zadd order 100 111
(integer) 1
127.0.0.1:6379 zadd order 120 112
(integer) 1
127.0.0.1:6379 zadd order 140 113
(integer) 1
127.0.0.1:6379 zadd order 170 114
(integer) 1
127.0.0.1:6379 zrangebyscore order 0 130
1) 111
2) 112
zrangeByScore在异步线程定时执行就行了这是延时任务的主动释放。而在组内应用的系统中还有一个监听消息的机制当接收到消息后需要取出sessionId将zset中对应的session元素删除这边就需要扫描zset所有元素便用到了zscan命令。
zscan
zscan是一个增量命令它在官网的定义如下 所谓增量就是不会一次全部而是返回一定数量的元素也就是上面指定的count然后返回cursor表示扫描到的位置只要这个cursor不为0就表示扫描没有结束这就是增量命令最重要的表现形式。
然而这是我们对增量的理解但是zset狗在对于元素数量比较少的时候也就是底层以ziplist组织的时候会忽视count一次返回所有元素而当以skiplist组织的时候才会返回count个如果没有传count默认10个。这也是此次组内系统故障的根因同事在用zscan的时候并没有传count但是元素数量超过了128个导致只扫描了10个后就停止了代码也没有继续从返回的cursor扫描导致了zset中存在大量的元素未被删除被延迟任务队列监控线程通过zrangeByScore扫描到错误地认为这些元素超时而返回了错误的系统信息。
从源码上看也可以看出一些端倪 这边看确实默认值是10但是直到我看到 当是skiplist的时候count会默认变成两倍但是在我的电脑上并没有这个现象可能是版本差异但是我找了之前的release描述没有找到相关的信息这个问题因为我太饿了就查不下去了其实是懒 有读者知道的可以后台私信感谢~
zset-max-ziplist-entries 3
127.0.0.1:6379 object encoding order
ziplist
127.0.0.1:6379 zscan order 0 match order* count 5
1) 0
2) 1) order-1112) 1003) order-1124) 1105) order-1136) 1207) order-1148) 1309) order-11510) 14011) order-11612) 15013) order-11814) 17015) order-11916) 18017) order-12018) 19019) order-12120) 20021) order-12222) 21023) order-12324) 220
127.0.0.1:6379 zadd order 230 order-124
(integer) 1
127.0.0.1:6379 object encoding order
skiplist
127.0.0.1:6379 zscan order 0
1) 5
2) 1) order-1232) 2203) order-1164) 1505) order-1186) 1707) order-1248) 2309) order-12110) 20011) order-11412) 13013) order-12014) 19015) order-11516) 14017) order-11118) 10019) order-12220) 210
发现确实只返回了10个并且cursor是5表示并没有结束至此我们复现了系统的问题现象也是一致的。
解决方案
方案一传一个很大的count
方案二zrange扫描全部代码内做筛选
方案三循环zscan直到cursor为0
业务方案zrangeByScore扫描到后继续保底
复盘
故障从监控预警到定位问题时间较长原因在于开发人员并没有直接定位到zscan的问题并且这部分命令是作为lua脚本执行调试困难。
流程上看这种问题无法通过单测发现确实需要开发人员本身对所用技术的深刻了解任何流程规则只能降低问题发生概率。
最后gpt给出的答案确实是生产方案 周末快乐分享一句最近看到的诗
“欲买桂花同载酒终不似少年游”