Redis 核心技术与实战
蒋德钧
中科院计算所副研究员
81696 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 53 讲
开篇词 (1讲)
实践篇 (28讲)
Redis 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

16 | 异步机制:如何避免单线程模型的阻塞?

从库加载RDB文件
AOF日志同步写
删除操作
读操作
关键路径上的操作
数据迁移
哈希槽信息传输
从库清空数据库、加载RDB文件
主库生成、传输RDB文件
AOF日志同步写
RDB快照文件生成
清空数据库
删除操作
集合全量查询和聚合操作
键值对的增删改查操作
网络IO
数据迁移
向其他实例传输哈希槽信息
从库接收RDB文件、清空数据库、加载RDB文件
主库生成、传输RDB文件
AOF日志重写
记录AOF日志
生成RDB快照
数据库操作
键值对增删改查操作
网络IO
异步AOF日志同步写
异步清空数据库
异步删除操作
Redis的异步子线程机制
AOF日志写操作的异步执行
异步删除和惰性删除
任务队列和子线程的交互
Redis主线程启动后创建的子线程
异步执行的要求
切片集群实例交互时的阻塞点
主从节点交互时的阻塞点
和磁盘交互时的阻塞点
和客户端交互时的阻塞点
切片集群实例
主从节点
磁盘
客户端
Redis缓冲区
Redis内存碎片
Redis关键系统配置
CPU核和NUMA架构的影响
Redis内部的阻塞式操作
硬件配置
关键机制
系统配置
命令操作
每课一问
小结
异步的子线程机制
哪些阻塞点可以异步执行
阻塞点的具体操作
Redis实例的阻塞点
影响Redis性能的潜在因素
Redis性能影响因素
避免单线程模型的阻塞
异步机制

该思维导图由 AI 生成,仅供参考

你好,我是蒋德钧。
Redis 之所以被广泛应用,很重要的一个原因就是它支持高性能访问。也正因为这样,我们必须要重视所有可能影响 Redis 性能的因素(例如命令操作、系统配置、关键机制、硬件配置等),不仅要知道具体的机制,尽可能避免性能异常的情况出现,还要提前准备好应对异常的方案。
所以,从这节课开始,我会用 6 节课的时间介绍影响 Redis 性能的 5 大方面的潜在因素,分别是:
Redis 内部的阻塞式操作;
CPU 核和 NUMA 架构的影响;
Redis 关键系统配置;
Redis 内存碎片;
Redis 缓冲区。
这节课,我们就先学习了解下 Redis 内部的阻塞式操作以及应对的方法。
第 3 讲中,我们学习过,Redis 的网络 IO 和键值对读写是由主线程完成的。那么,如果在主线程上执行的操作消耗的时间太长,就会引起主线程阻塞。但是,Redis 既有服务客户端请求的键值对增删改查操作,也有保证可靠性的持久化操作,还有进行主从复制时的数据同步操作,等等。操作这么多,究竟哪些会引起阻塞呢?
别着急,接下来,我就带你分门别类地梳理下这些操作,并且找出阻塞式操作。

Redis 实例有哪些阻塞点?

Redis 实例在运行时,要和许多对象进行交互,这些不同的交互就会涉及不同的操作,下面我们来看看和 Redis 实例交互的对象,以及交互时会发生的操作。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Redis是一个高性能的数据库,但其单线程模型可能会受到阻塞式操作的影响。本文介绍了Redis内部的阻塞式操作及相应的应对方法。文章首先介绍了Redis实例与客户端、磁盘、主从节点和切片集群实例之间的交互关系,并分析了可能引起阻塞的操作。针对这些阻塞点,文章提出了五大阻塞式操作,包括集合全量查询和聚合操作、bigkey删除、清空数据库、AOF日志同步写以及从库加载RDB文件。为了避免这些阻塞式操作影响主线程的服务能力,Redis提供了异步线程机制,将一些任务交给子线程后台完成,从而避免阻塞主线程。文章还介绍了Redis的异步子线程机制,以及对应的操作建议。总的来说,通过本文的总结,读者可以快速了解Redis内部阻塞式操作的特点以及相应的解决方法。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 核心技术与实战》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(49)

  • 最新
  • 精选
  • Kaito
    Redis的写操作(例如SET,HSET,SADD等)是在关键路径上吗? 我觉得这需要客户端根据业务需要来区分: 1、如果客户端依赖操作返回值的不同,进而需要处理不同的业务逻辑,那么HSET和SADD操作算关键路径,而SET操作不算关键路径。因为HSET和SADD操作,如果field或member不存在时,Redis结果会返回1,否则返回0。而SET操作返回的结果都是OK,客户端不需要关心结果有什么不同。 2、如果客户端不关心返回值,只关心数据是否写入成功,那么SET/HSET/SADD不算关键路径,多次执行这些命令都是幂等的,这种情况下可以放到异步线程中执行。 3、但是有种例外情况,如果Redis设置了maxmemory,但是却没有设置淘汰策略,这三个操作也都算关键路径。因为如果Redis内存超过了maxmemory,再写入数据时,Redis返回的结果是OOM error,这种情况下,客户端需要感知有错误发生才行。 另外,我查阅了lazy-free相关的源码,发现有很多细节需要补充下: 1、lazy-free是4.0新增的功能,但是默认是关闭的,需要手动开启。 2、手动开启lazy-free时,有4个选项可以控制,分别对应不同场景下,要不要开启异步释放内存机制: a) lazyfree-lazy-expire:key在过期删除时尝试异步释放内存 b) lazyfree-lazy-eviction:内存达到maxmemory并设置了淘汰策略时尝试异步释放内存 c) lazyfree-lazy-server-del:执行RENAME/MOVE等命令或需要覆盖一个key时,删除旧key尝试异步释放内存 d) replica-lazy-flush:主从全量同步,从库清空数据库时异步释放内存 3、即使开启了lazy-free,如果直接使用DEL命令还是会同步删除key,只有使用UNLINK命令才会可能异步删除key。 4、这也是最关键的一点,上面提到开启lazy-free的场景,除了replica-lazy-flush之外,其他情况都只是*可能*去异步释放key的内存,并不是每次必定异步释放内存的。 开启lazy-free后,Redis在释放一个key的内存时,首先会评估代价,如果释放内存的代价很小,那么就直接在主线程中操作了,没必要放到异步线程中执行(不同线程传递数据也会有性能消耗)。 什么情况才会真正异步释放内存?这和key的类型、编码方式、元素数量都有关系(详细可参考源码中的lazyfreeGetFreeEffort函数): a) 当Hash/Set底层采用哈希表存储(非ziplist/int编码存储)时,并且元素数量超过64个 b) 当ZSet底层采用跳表存储(非ziplist编码存储)时,并且元素数量超过64个 c) 当List链表节点数量超过64个(注意,不是元素数量,而是链表节点的数量,List的实现是在每个节点包含了若干个元素的数据,这些元素采用ziplist存储) 只有以上这些情况,在删除key释放内存时,才会真正放到异步线程中执行,其他情况一律还是在主线程操作。 也就是说String(不管内存占用多大)、List(少量元素)、Set(int编码存储)、Hash/ZSet(ziplist编码存储)这些情况下的key在释放内存时,依旧在主线程中操作。 可见,即使开启了lazy-free,String类型的bigkey,在删除时依旧有阻塞主线程的风险。所以,即便Redis提供了lazy-free,我建议还是尽量不要在Redis中存储bigkey。 个人理解Redis在设计评估释放内存的代价时,不是看key的内存占用有多少,而是关注释放内存时的工作量有多大。从上面分析基本能看出,如果需要释放的内存是连续的,Redis作者认为释放内存的代价比较低,就放在主线程做。如果释放的内存不连续(大量指针类型的数据),这个代价就比较高,所以才会放在异步线程中去执行。 如果我的理解有偏差,还请老师和大家指出!
    2020-09-14
    81
    559
  • 注定非凡
    1,作者讲了什么? Redis有哪些可能导致阻塞的操作,以及解决机制 2,作者是怎么把这件事讲明白的? 1,分类:通过Redis实例的交互关系,拆分了四类:客户端,磁盘,主从集群,分片集群 2,提出关键路径操作概念,作为是否可以异步处理的判断标准 3,为了讲明白,作者讲了哪些要点?有哪些亮点? 1,亮点1:将Redis的可能阻塞点划分了四块,与客户端交互,与磁盘交互,主从实例交互,分片集群实例交互 2,亮点2:通过关键路径上操作概念,建立了判断是否适合异步操作的标准 3,要点1:与客户端交互时的阻塞点:高复杂度的增删改操作(集合全量的查询和聚合操作,bigKey 删除操作,清空数据库) 4,要点2:与磁盘交互的阻塞点:aof日志实时同步写回 5,要点3:主从节点交互时的阻塞点:从库加载RDB文件 6,要点4:分片集群的阻塞点:哈希槽bigkey数据迁移 7,要点5:关键路径操作概念:客户端把请求发给Redis后,要等着Redis返回数据结果的操作 8,要点6:异步的子线程机制:主线程通过一个链表形式的任务队列和子线程进行交互 4,对于作者所讲,我有哪些发散性思考? 给自己提了几个问题: 1,Redis至少有几个线程? 2,网络IO有时会比较慢,网络IO是否为Redis的阻塞点? 3,删除操作为什么会阻塞Redis 5,将来在哪些场景里,我能够使用它? 6,留言收获 什么时候Redis会真正的异步释放内存?(答案来自@kaito 大佬) lazy free机制:Redis收到键值对删除和清空数据库的指令时,主线线程会把这个操作封装成一个任务,放入任务队列中,然后给客户端返回一个完成信息,但实际上,这个删除还没有执行,需要等待后台子线程从任务队列中读取到这个任务后,才开始实际删除键值对,并释放相应的内存空间。 但是:lazy-free是4.0新增功能,默认关闭。开启这个配置后, 除了replica-lazy-flush之外,其他情况都只是*可能*去异步释放key的内存,并不是每次必定异步释放内存的。是否会真正异步释放内存,取决于key的类型,编码方式,元素数量,所以 即使开启了lazy-free,String类型的bigkey,在删除时依旧有阻塞主线程的风险
    2020-10-21
    39
  • Spring4J
    Redis的异步子线程机制就跟java里面的线程池原理差不多,都是主线程封装任务到队列中,子线程到队列中拉取任务异步执行,运用了生产者消费者的模型
    2020-09-14
    11
  • 在删除一个 大 key 的时候,redis 把这个任务放到队列,实际还没有执行删除操作,这时候马上来个查询查刚才删除的 key. 这时候会查询到吗? Redis 是怎么处理这种情况的?
    2020-09-15
    3
    9
  • 释放掉的内存块插入一个空闲内存块的链表 =================== 这个过程怎么会耗时间呢? 插入链表的时候,不就是在链表尾部一放不就可以了吗? 时间复杂度位 O(1)
    2020-09-15
    5
    6
  • 漫步oo0云端
    今天学习了5种阻塞点,请问老师,后面会学习,当redis发生阻塞时如何分析是什么操作导致的这个技能吗?
    2020-09-14
    1
    6
  • Geek_1b4c8
    异步删除key时,如果删除操作在任务队列里面还未被子线程执行,此时如果来了get查询操作,是否会返回数据吗,异步删除时是否会先标识为删除吗
    2022-04-08
    1
    2
  • test
    写操作是否在关键路径,需要看使用方是否需要确认写入已经完成。
    2020-09-14
    2
  • 那时刻
    当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到任务队列中。后台子线程读取任务后,然后写入AOF日志。请问老师,如果写入操作比较频繁,是否也会引起redis延迟增大呢?
    2020-09-14
    1
    2
  • Geek_77c5bf
    老师我一直有个问题,你上面说,如果是读操作。读操作是主线程来处理查询并返回的吗?意思是同步的?我的理解,客户端的确是一直在等待结果,但是redis是异步的,是基于事件通知回调机制,等redis查到结果,再socket通知客户端。希望老师帮忙解答,困扰我很久
    2021-03-31
    2
    1
收起评论
显示
设置
留言
49
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部