Redis核心技术与实战
蒋德钧
中科院计算所副研究员
立即订阅
5433 人已学习
课程目录
已更新 6 讲 / 共 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 这样学Redis,才能技高一筹
免费
基础篇 (5讲)
01 | 基本架构:一个键值数据库包含什么?
02 | 数据结构:快速的Redis有哪些慢操作?
03 | 高性能IO模型:为什么单线程Redis能那么快?
04 | AOF日志:宕机了,Redis如何避免数据丢失?
05 | 内存快照:宕机后,Redis如何实现快速恢复?
Redis核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

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

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

Redis 为什么用单线程?

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

多线程的开销

日常写程序时,我们经常会听到一种说法:“使用多线程,可以增加系统吞吐率,或是可以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即吞吐率。下面的左图是我们采用多线程时所期待的结果。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Redis核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥29.9
立即订阅
登录 后留言

精选留言(35)

  • 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
    3
    78
  • Darren
    1.big key的操作。
    2.潜在的大量数据操作,比如 key *或者get all之类的操作,所以才引入了scan的相关操作。
    3.特殊的场景,大量的客户端接入。


    简单介绍下select poll epoll的区别,select和poll本质上没啥区别,就是文件描述符数量的限制,select根据不同的系统,文件描述符限制为1024或者2048,poll没有数量限制。他两都是把文件描述符集合保存在用户态,每次把集合传入内核态,内核态返回ready的文件描述符。
    epoll是通过epoll_create和epoll_ctl和epoll_await三个系统调用完成的,每当接入一个文件描述符,通过ctl添加到内核维护的红黑树中,通过事件机制,当数据ready后,从红黑树移动到链表,通过await获取链表中准备好数据的fd,程序去处理。
    2020-08-10
    4
    19
  • test
    单线程同步非阻塞读取网络IO的时候会有性能瓶颈,如果读取的内容过多的时候
    2020-08-10
    3
  • 一步
    Redis的事件处理队列只有一个吗?不同的事件的优先级都是一样的吗?只是简单的按照对接的先进先出的特性依次进行处理的吗?
    2020-08-11
    2
  • 咸鱼
    这章让我对IO多路复用的理解又深了些
    2020-08-10
    2
  • 每天晒白牙
    Redis 的单线程指 Redis 的网络 IO 和键值对读写由一个线程来完成的(这是 Redis 对外提供键值对存储服务的主要流程)
    Redis 的持久化、异步删除、集群数据同步等功能是由其他线程而不是主线程来执行的,所以严格来说,Redis 并不是单线程

    为什么用单线程?
    多线程会有共享资源的并发访问控制问题,为了避免这些问题,Redis 采用了单线程的模式,而且采用单线程对于 Redis 的内部实现的复杂度大大降低

    为什么单线程就挺快?
    1.Redis 大部分操作是在内存上完成,并且采用了高效的数据结构如哈希表和跳表
    2.Redis 采用多路复用,能保证在网络 IO 中可以并发处理大量的客户端请求,实现高吞吐率

    Redis 6.0 版本为什么又引入了多线程?
    Redis 的瓶颈不在 CPU ,而在内存和网络,内存不够可以增加内存或通过数据结构等进行优化
    但 Redis 的网络 IO 的读写占用了发部分 CPU 的时间,如果可以把网络处理改成多线程的方式,性能会有很大提升
    所以总结下 Redis 6.0 版本引入多线程有两个原因
    1.充分利用服务器的多核资源
    2.多线程分摊 Redis 同步 IO 读写负荷

    执行命令还是由单线程顺序执行,只是处理网络数据读写采用了多线程,而且 IO 线程要么同时读 Socket ,要么同时写 Socket ,不会同时读写
    2020-08-13
    1
  • Mr.蜜
    忘记提另一个问题,既然老师说道select和epoll,为什么不提一下poll呢?
    2020-08-12
    1
    1
  • 注定非凡
    1,作者讲了什么?
    redis实现单线程实现高性能IO的设计机制
    2,作者是怎么把这事给讲明白的?
    作者首先从简单的网络通信socket讲起,引出了非阻塞socket,由此谈到了著名的I/O多路复用,Linux内核的select/epoll机制
    3,为了讲明白,作者讲了哪些要点?有哪些亮点?
    (1)首先声明“redis单线程”这个概念的具体含义
    (2)引入具体业务场景:redis的数据读取,事件处理机制模型
    (3)解析单线程相对多线程带来的优势,已及多线程所特有的问题
    (4)基于redis单线程的,设计机制,引出了网络socket的问题
    2020-08-11
    1
  • 曾轼麟
    虽然单线程很快,没有锁的单线程更快借助CPU的多级缓存可以把性能发挥到最大。但是随着访问量的增加,以及数据量的增加,IO的写入写出会成为性能瓶颈。10个socket的IO吞吐处理肯定比1000个socket吞吐处理的快,为了解决这个问题,Redis6引入了IO多线程的方式以及client缓冲区,在实际指令处理还是单线程模式。在IO上变成的了【主线程】带着众多【IO线程】进行IO,IO线程听从主线程的指挥是写入还是写出。Read的时候IO线程会和主线程一起读取并且解析命令(RESP协议)存入缓冲区,写的时候会从缓冲区写出到Socket。IO线程听从主线程的指挥,在同一个时间点上主线程和IO线程会一起写出或者读取,并且主线程会等待IO线程的结束。但是这种模式的多线程会面临一给NUMA陷阱的问题,在最近的Redis版本中加强了IO线程和CPU的亲和性解决了这个问题。(不过目前官方在默认情况下并不推荐使用多线程IO模式,需要手动开启)
    2020-08-10
    1
  • 每天晒白牙
    理解的深入了
    2020-08-10
    2
    1
  • zhou
    老师分析的 redis io 模型中,redis 线程是循环处理每个事件的。如果其中一个事件比较耗时,会影响后面事件的及时处理。
    2020-08-14
  • Mr.蜜
    首先看完课题,发现几个问题,Redis的持久化到底是多线程,还是子进程?copy on write又是怎么用在持久化中的?第二个,BIO和NIO看了半天才反应过来,难道他不叫Block IO和non Block IO吗?第三个算是回答,Redis6.0中的多线程是IO多线程,就是说Redis把网络消息的收发让其他线程去处理,这样无疑可以让Redis的工作线程只处理与数据有关的事务,而把数据的收发全让其他线程去处理,这样做就意味着Redis6.0的性能更好。
    2020-08-12
  • gerry pang
    老师,我有一点没太明白,
    ‘’Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。‘’
    我看例如镜像文件的创建,redis不是后台fork出子进程吗?
    2020-08-12
  • 子龙
    1. 单线程是指多个连接或者请求使用同一个线程处理,类似浏览器多个用户的点击使用同一个线程处理
    2. 单线程减少多线程引入的访问控制带来的开销,例如同步访问共享数据
    3. IO 模型是OS的IO模型,Redis只是使用了这种比较友好的Epoll模型,由系统负责监听客户端的连接等,通知Redis工作线程,属于流程优化。
    2020-08-11
  • humor
    当 Linux 内核监听到有连接请求或读数据请求时,就会触发 Accept 事件和 Read 事件,此时,内核就会回调 Redis 相应的 accept 和 get 函数进行处理。
    这里的accept和get函数的调用是redis在应用侧调用的,而不是linux内核调用的吧,因为linux内核只是通知redjs有事件到达了,需要处理
    2020-08-11
  • LindaWang
    网络I/O瓶颈:随着业务场景越来越复杂,需要更大的QPS,如果部署过多的Redis服务器就会导致管理的Redis服务器过多,维护代价大。采用多线程,可以充分利用多核分摊Redis同步I/O读写负荷
    2020-08-11
  • 第四范式
    请教一个问题。“Redis 单线程对该事件队列不断进行处理。”这句话的意思是。对于某个事件的回调函数如果处理的时间较多。就会造成线程阻塞吧。疑问如下:(1)事件必须处理完某一个,下一个才 继续进行吧?(2) 线程来处理IO。不算阻塞。只有在处理IO之前,等待数据来的时间,才算阻塞?希望老师帮忙解答答一下。谢谢您。
    2020-08-11
  • 天 雨 心
    为啥redis单线程这么高效?1.采用select/poll的网络模型NIO多路复用机制 2.底层采用哈希表和跳表等数据结构 3.单线程避免共享资源竞争 欢迎补充
    2020-08-11
  • 密码123456
    我总感觉,还是多线程快一点。比如,数据的读取,数据的写入,命令的解析。都可以使用多线程。然后写入一个队列,然后单线程进行,查找,写入,修改。把结果写入队列。然后让多线程发送给客户端。我觉得这样会快一点。一个线程干完所有的活,总觉得没那么快。是不是我理解的方式不对?
    2020-08-11
  • 天 雨 心
    那redis6.0为啥使用多线程?网络请求这块是多线程吧,多路复用机制selector是单线程(可以调优成多线程么,和netty一样),处理每一个请求是多个的去处理。
    2020-08-11
收起评论
35
返回
顶部