这一次,吃了Redis的亏,也败给了GPT

关注【离心计划】,一起离开地球表面

 

背景

组内有一个系统中有一个延迟任务的需求,关于延迟任务常见的做法有时间轮、延迟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-111"
    2) "100"
    3) "order-112"
    4) "110"
    5) "order-113"
    6) "120"
    7) "order-114"
    8) "130"
    9) "order-115"
   10) "140"
   11) "order-116"
   12) "150"
   13) "order-118"
   14) "170"
   15) "order-119"
   16) "180"
   17) "order-120"
   18) "190"
   19) "order-121"
   20) "200"
   21) "order-122"
   22) "210"
   23) "order-123"
   24) "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-123"
    2) "220"
    3) "order-116"
    4) "150"
    5) "order-118"
    6) "170"
    7) "order-124"
    8) "230"
    9) "order-121"
   10) "200"
   11) "order-114"
   12) "130"
   13) "order-120"
   14) "190"
   15) "order-115"
   16) "140"
   17) "order-111"
   18) "100"
   19) "order-122"
   20) "210"

发现确实只返回了10个,并且cursor是5,表示并没有结束,至此我们复现了系统的问题,现象也是一致的。

解决方案

方案一:传一个很大的count

方案二:zrange扫描全部,代码内做筛选

方案三:循环zscan,直到cursor为0

业务方案:zrangeByScore扫描到后继续保底

复盘

故障从监控预警到定位问题时间较长,原因在于开发人员并没有直接定位到zscan的问题,并且这部分命令是作为lua脚本执行,调试困难。

流程上看,这种问题无法通过单测发现,确实需要开发人员本身对所用技术的深刻了解,任何流程规则只能降低问题发生概率。

最后,gpt给出的答案确实是生产方案

周末快乐,分享一句最近看到的诗

欲买桂花同载酒,终不似,少年游

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

好用的5款国产低代码平台介绍

一、云程低代码平台 云程低代码平台是一款基于springboot、vue.js技术的企业级低代码开发平台,平台采用模型驱动、高低码融合、开放扩展等设计理念,基于业务建模、流程建模、表单建模、报表建模、大屏建模等可视化建模工具,通过拖拉拽零代码方…

安装flume

flume最主要的作用就是实时读取服务器本地磁盘的数据,将数据写入到hdfs中架构:开始安装一,上传压缩包,解压并更名解压:[rootsiwen install]# tar -zxf apache-flume-1.9.0-bin.tar.gz -C ../soft/[rootsiwen install]#…

太强了,英伟达面对ChatGPT还有这一招...

大家好,我是 Jack。 今年可谓是 AI 元年,ChatGPT、AIGC、VITS 都火了一波。 我也先后发布了这几期视频: 这是一个大模型的时代,AI 能在文本、图像、音频等领域大放异彩,得益于大模型。而想要预训练大模型&#xff0c…

nodejs篇 express(1)

文章目录前言express介绍安装RESTful接口规范express的简单使用一个最简单的服务器,仅仅只需要几行代码便可以实现。restful规范的五种接口类型请求信息req的获取响应信息res的设置中间件的使用自定义中间件解决跨域nodejs相关其它内容前言 express作为nodejs必学的…

前缀树(字典树/Trie) -----Java实现

目录 一.前缀树 1.什么是前缀树 2.前缀树的举例 二.前缀树的实现 1.前缀树的数据结构 1.插入字符串 2.查找字符串 3.查找前缀 三.词典中最长的单词 1.题目描述 2.问题分析 3.代码实现 一.前缀树 1.什么是前缀树 字典树(Trie树)是一种树形…

机器学习——无监督学习

机器学习的分类一般分为下面几种类别:监督学习( supervised Learning )无监督学习( Unsupervised Learning )强化学习( Reinforcement Learning,增强学习)半监督学习( Semi-supervised Learning )深度学习(Deep Learning)Python Scikit-learn. http: // …

用Pytorch构建一个喵咪识别模型

本文参加新星计划人工智能(Pytorch)赛道:https://bbs.csdn.net/topics/613989052 目录 一、前言 二、问题阐述及理论流程 2.1问题阐述 2.2猫咪图片识别原理 三、用PyTorch 实现 3.1PyTorch介绍 3.2PyTorch 构建模型的五要素 3.3PyTorch 实现的步骤 3.3.…

app自动化测试——app自动化控制、常见控件定位方法

文章目录一、app自动化控制1、清理数据:2、启动:3、关闭:二、常见控件定位方法1、android知识2、ios 基础知识3、元素定位4、控件基础知识5、app dom 结构解析6、iOS 与 Android dom 结构的区别7、定位方法测试步骤三要素定位方式&#xff1a…

大环境不好,找工作太难?三面阿里,幸好做足了准备,已拿offer

三面大概九十分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我狂补知识)分为软件测试基础、Python自动化…

超专业解析!10分钟带你搞懂Linux中直接I/O原理

我们先看一张图: 这张图大体上描述了 Linux 系统上,应用程序对磁盘上的文件进行读写时,从上到下经历了哪些事情。 这篇文章就以这张图为基础,介绍 Linux 在 I/O 上做了哪些事情。 文件系统 什么是文件系统 文件系统&#xff0…

docker版jxTMS使用指南:数据查询

本文讲解docker版jxTMS的数据查询,整个系列的文章请查看:docker版jxTMS使用指南 请按前文所述先做好相关的准备工作,然后多在helloWorld界面输入各种数据后点【点我】按钮,以多创建点数据来为查询做下准备。 分页查询 首先在we…

python网上选课系统django-PyCharm

学生选课信息管理系统,可以有效的对学生选课信息、学生个人信息、教师个人信息等等进行管理。 开发语言:Python 框架:django Python版本:python3.7.7 数据库:mysql 数据库工具:Navicat11 开发软件&#x…

RK3588平台开发系列讲解(NPU篇)NPU调试方法

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、日志等级二、NPU 支持查询设置项沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起来看一下NPU的调试方法。 一、日志等级 NPU 的运行库会根据开发板上的系统环境变量输出一些日志信息或者生成…

操作系统(2.4.5)--管程机制

1.管程的定义 利用共享数据结构抽象地表示系统中的共享资源,而把对该共享数据结构实施的操作定义为一组过程进程对共享资源的申请、释放和其它操作,都是通过这组过程对共享数据结构的操作来实现的,这组过程还可以根据资源的情况,或…

yolov8训练筷子点数数据集

序言 yolov8发布这么久了,一直没有机会尝试一下,今天用之前自己制作的筷子点数数据集进行训练,并且记录一下使用过程以及一些常见的操作方式,供以后翻阅。 一、环境准备 yolov8的训练相对于之前的yolov5简单了很多,…

【链表OJ题(九)】环形链表延伸问题以及相关OJ题

环形链表OJ题 1. 环形链表 链接:141. 环形链表 描述: 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环&…

简单分析Linux内核基础篇——initcall

写过Linux驱动的人都知道module_init宏,因为它声明了一个驱动的入口函数。 除了module_init宏,你会发现在Linux内核中有许多的驱动并没有使用module_init宏来声明入口函数,而是看到了许多诸如以下的声明: static int __init qco…

Java之类与对象(图文结合)

目录 一、面向对象的初步认知 1、什么是面向对象 2、面向对象与面向过程 二、类定义和使用 1、简单认识类 2、类的定义格式 3、练习 (1)定义一个狗类 (2)定义一个学生类 三、类的实例化 1、什么是实例化 2、类和对象的…

CSDN 周赛38期题解

CSDN 周赛38期题解1、题目名称:代写匿名信2、题目名称:寻因找祖3、题目名称:小Q新式棋盘4、题目名称:拯救公主结束语1、题目名称:代写匿名信 小Q想要匿名举报XX领导不务正业! 小Q害怕别人认出他的字迹。 他…

【数据结构】Java实现双向链表

目录 1. 接口的实现 2. 动手实现双链表 2.1 重写SeqList接口方法 2.2 在当前链表尾部添加节点(尾插) 2.3 在当前链表头部添加节点(头插) 2.4 检验index是否合法 2.5 在 第index位置添加节点(任意位置) 2.6 …