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

03 | 高性能IO模型:为什么单线程Redis能那么快?

针对不同事件的处理函数
事件队列
select/epoll机制
一个线程处理多个IO流
已连接套接字
监听套接字
recv()
accept()
高吞吐率
网络IO操作并发处理
跳表
哈希表
提高系统易调试性和可维护性
避免多线程并发控制问题
瓶颈在于共享资源的并发访问控制问题
随着线程增加,吞吐率增长迟缓
多线程系统吞吐率增加
其他功能由额外线程执行
网络IO和键值对读写由一个线程完成
Redis 6.0的多线程模型
单线程高性能与多路复用IO模型密切相关
Redis单线程设计的核心原因
基于事件的回调机制
IO多路复用机制
非阻塞模式
网络IO操作潜在阻塞点
多路复用机制
高效的数据结构
大部分操作在内存上完成
Redis为何采用单线程
多线程开销
Redis的单线程特点
小结
基于多路复用的高性能I/O模型
基本IO模型与阻塞点
单线程Redis高性能原因
Redis单线程设计
高性能IO模型:为什么单线程Redis能那么快?

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

你好,我是蒋德钧。
今天,我们来探讨一个很多人都很关心的问题:“为什么单线程的 Redis 能那么快?”
首先,我要和你厘清一个事实,我们通常说,Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
所以,严格来说,Redis 并不是单线程,但是我们一般把 Redis 称为单线程高性能,这样显得“酷”些。接下来,我也会把 Redis 称为单线程模式。而且,这也会促使你紧接着提问:“为什么用单线程?为什么单线程能这么快?”
要弄明白这个问题,我们就要深入地学习下 Redis 的单线程设计机制以及多路复用机制。之后你在调优 Redis 性能时,也能更有针对性地避免会导致 Redis 单线程阻塞的操作,例如执行复杂度高的命令。
好了,话不多说,接下来,我们就先来学习下 Redis 采用单线程的原因。

Redis 为什么用单线程?

要更好地理解 Redis 为什么用单线程,我们就要先了解多线程的开销。

多线程的开销

日常写程序时,我们经常会听到一种说法:“使用多线程,可以增加系统吞吐率,或是可以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即吞吐率。下面的左图是我们采用多线程时所期待的结果。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Redis作为单线程高性能的特点,得益于其精巧的设计和多路复用机制。文章首先解释了Redis采用单线程模型的原因,指出多线程开发中的共享资源并发访问控制问题会带来额外开销,降低系统吞吐率,而采用多线程还会降低系统代码的易调试性和可维护性,因此Redis直接采用了单线程模式。然后,文章详细介绍了Redis采用单线程模型如何实现高性能,主要包括大部分操作在内存上完成、采用高效的数据结构和多路复用机制。在多路复用机制方面,文章重点介绍了基本IO模型与阻塞点,以及非阻塞模式的设置,以保证Redis线程不会在阻塞点一直等待,也不会导致Redis无法处理实际到达的连接请求或数据。文章内容深入浅出,为读者解释了Redis单线程高性能的原因,使读者能够快速了解Redis的技术特点。 文章还提到了Redis 6.0中提出了多线程模型,这可能引入复杂的并发控制问题,而关于这些问题,作者承诺在后续课程中进行详细介绍。整体而言,本文通过对Redis单线程模型和多路复用机制的解释,为读者提供了对Redis高性能特点的深入理解。

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

全部留言(139)

  • 最新
  • 精选
  • 柳磊
    作者您好,引用文中一段话“我们知道了,Redis 单线程是指它对网络 IO 和数据读写的操作采用了一个线程”,我有个疑问,redis为什么要网络IO与业务处理(读写)用一个线程?而不用Netty中常见的Reactor线程模型,把io线程(netty中的boss线程)与业务处理线程(netty中的work线程)分开,业务处理线程只开启一个线程,也不会有共享资源竞争的问题。

    作者回复: 在Redis 6.0版本前,Redis用一个线程实现网络请求的解析和读写处理,我个人觉得主要还是这种线程模型实现简单。 不过随着网络硬件越来越快后,网络请求收发更快了,所以从Redis 6.0开始,网络请求解析也是由专门的线程处理,从而支持快速网络读写。而读写处理仍然由单个主线程执行,这是为了避免多线程协调的开销。

    2020-12-15
    3
    56
  • zhou
    老师分析的 redis io 模型中,redis 线程是循环处理每个事件的。如果其中一个事件比较耗时,会影响后面事件的及时处理。

    作者回复: 是的,所以如果有慢操作的话,就会影响其他操作了。

    2020-08-14
    13
  • 竹真
    作者您好,读完您的文章还有点疑惑,Redis读取客户端数据和读内存是一个线程?

    作者回复: Redis 6.0前的版本是用一个线程来读取网络请求并进行解析,并根据请求的具体命令操作进行数据读写的。Redis 6.0开始,网络请求的解析可以用多线程来执行,但是读写内存还是一个线程。

    2020-11-06
    3
    12
  • spoofer
    还有的性能瓶颈:即使io复用已经很牛叉了,但是redis线程始终是用户线程,要读取或者写入网络数据,还是要进行read和write的系统调用的,而系统调用是一个耗时的操作。so~~ 为什么6.0要引入多线程?一个原因就是要找些小弟来处理这个耗时的数据读写啊~~~

    作者回复: 耗时的系统调用,这个对高性能系统来说是个潜在瓶颈。

    2020-11-05
    9
  • BrightLoong
    Redis 单线程是指它对网络 IO 和数据读写的操作采用了一个线程 对这句话不是很理解,总觉得是处理网络IO是一个线程,然后把事件放入队列;读写操作又是一个线程,从队列中处理请求。求解答

    作者回复: 在Redis 6.0前,网络请求的解析和数据读写都是由主线程来完成的,这也是我们称之为单线程的原因。 从Redis 6.0开始,网络请求的解析是由其他线程完成,然后把解析后的请求交由主线程进行实际的内存读写。

    2020-11-12
    7
    7
  • 滴流乱转小胖儿
    老师你好,单线程的处理事件队列中的事件,这样还是会遇到性能瓶颈吧?

    作者回复: 如果这个事件处理本身很耗时,例如对一个大集合的key进行全量操作(比如SMEMBERS, HGETALL),这也是会引起性能瓶颈的。

    2020-08-10
    4
  • 华仔
    Redis在Linux上应该是单进程,不能说是单线程,main函数完成初始化后就进入事件循环处理,快照的时候也是直接fork一个新进程,而不是pthread_create一个新线程,请老师确认一下

    作者回复: 称为单线程的进程可能更合适些,进程中处理读写操作的就是一个线程。也可以看下第10讲常见问题答疑中的第二个问题。 快照时是用的fork,创建新的子进程。

    2020-11-29
    2
    2
  • LovePeace
    请问文章提到的套接字是指socket吗?

    作者回复: 是的,就是指socket。

    2020-10-16
    1
  • 0bug
    操作大key的时候,IO是性能瓶颈

    作者回复: 没错,bigkey是一直要关注的问题,再和单线程模型一叠加,容易出事。

    2020-08-10
    2
    1
  • Kaito
    Redis单线程处理IO请求性能瓶颈主要包括2个方面: 1、任意一个请求在server中一旦发生耗时,都会影响整个server的性能,也就是说后面的请求都要等前面这个耗时请求处理完成,自己才能被处理到。耗时的操作包括以下几种: a、操作bigkey:写入一个bigkey在分配内存时需要消耗更多的时间,同样,删除bigkey释放内存同样会产生耗时; b、使用复杂度过高的命令:例如SORT/SUNION/ZUNIONSTORE,或者O(N)命令,但是N很大,例如lrange key 0 -1一次查询全量数据; c、大量key集中过期:Redis的过期机制也是在主线程中执行的,大量key集中过期会导致处理一个请求时,耗时都在删除过期key,耗时变长; d、淘汰策略:淘汰策略也是在主线程执行的,当内存超过Redis内存上限后,每次写入都需要淘汰一些key,也会造成耗时变长; e、AOF刷盘开启always机制:每次写入都需要把这个操作刷到磁盘,写磁盘的速度远比写内存慢,会拖慢Redis的性能; f、主从全量同步生成RDB:虽然采用fork子进程生成数据快照,但fork这一瞬间也是会阻塞整个线程的,实例越大,阻塞时间越久; 2、并发量非常大时,单线程读写客户端IO数据存在性能瓶颈,虽然采用IO多路复用机制,但是读写客户端数据依旧是同步IO,只能单线程依次读取客户端的数据,无法利用到CPU多核。 针对问题1,一方面需要业务人员去规避,一方面Redis在4.0推出了lazy-free机制,把bigkey释放内存的耗时操作放在了异步线程中执行,降低对主线程的影响。 针对问题2,Redis在6.0推出了多线程,可以在高并发场景下利用CPU多核多线程读写客户端数据,进一步提升server性能,当然,只是针对客户端的读写是并行的,每个命令的真正操作依旧是单线程的。
    2020-08-10
    82
    1367
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部