redis线程模型

一.Reactor线程模型简介

Reactor模型都是 利用IO多路复用接收客户端请求。不同点在于接受请求后的处理。

1.1 单线程Reactor

从负责接收请求、读取请求到命令执行再到响应请求都是 单个线程执行

1.2 单线程 Reactor,工作者线程池模式

单个线程负责接收请求、读取请求和响应请求。 多线程执行计算。

1.3  R主从Reactor多线程模式线程模式

将Reactor分为mainReactor和subReactor。

  • Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件
  • 当Acceptor处理连接事件后,MainReactor将连接分配给SubReactor
  • SubReactor将连接加入到连接队列进行监听,并创建Handler进行各种事件处理
  • 当有新的事件发生,SubReactor就会调用对应的Handler处理
  • Handler通过read读取数据,分发给后面的worker线程处理
  • worker线程池分配独立的worker线程进行处理,并返回结果
  • handler收到响应结果后,再通过send将结果返回给client
  • Reactor主线程可以对应多个Reactor子线程,即MainReactor可以关联多个SubReactor
     

二. redis单线程模型

Redis 基于 Reactor 模式开发了自己的网络事件处理器 - 文件事件处理器(file event handler,后文简称为 FEH),而该处理器又是单线程的,所以 redis 设计为单线程模型。

文件事件分派器从队列中接收 I/O 多路复用程序传来的socket,并根据socket产生的事件类型,调用相应的事件处理器。当上一个socket产生的事件被对应事件处理器执行完后,文件事件分派器才会向队列拉取下一个要处理的socket,保证socket操作一定不会并发的执行,保证线程安全。

Redis所谓的单线程并不是所有工作都是只有一个线程在执行,而是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理。


这就是所谓的“单线程”。这也是Redis对外提供键值存储服务的主要流程。由于Redis在处理命令的时候是单线程作业的,所以会有一个Socket队列,每一个到达的服务端命令来了之后都不会马上被执行,而是进入队列,然后被线程的事件分发器逐个执行。

文件事件处理器的几个组成部分:

IO多路复用

Redis 的 I/O 多路复用程序的所有功能都是通过包装常见的 select、epoll、evport 和 kqueue 这些 I/O 多路复用函数库实现的,不同系统不同函数。 liunx采用 epoll函数。

三. redis多线程模型

redis 6.0 引入多线程。

3.1. 6.0之前真的是单线程么

6.0之前单线程值的是处理客户端请求是单线程的。 但是对于持久化,主动同步,大key处理,数据过期等功能都是其他线程完成

3.2. 6.0 多线程含义

redis处理接受客户端请求后,可以简化为三部分操作:

  • read 读区请求
  • parse->resp>command excute 解析请求到命令执行
  • write 相应请求

6.0之前,以上三个步骤依次执行。

6.0之后,read和write两部分支持多线程,第二部处理指令依然是单线程。

6.0版本优化之后,主线程和多线程网络IO的执行流程如下:

具体步骤如下:

  • 主线程建立连接,并接受数据,并将获取的 socket 数据放入等待队列;
  • 通过轮询的方式将 socket读取出来并分配给 IO 线程;
  • 之后主线程保持阻塞,一直等到 IO 线程完成 socket 读取和解析;
  • I/O 线程读取和解析完成之后,返回给主线程 ,主线程开始执行 Redis 命令;
  • 执行完Redis命令后,主线程阻塞,直到IO 线程完成 结果回写到socket 的工作;
  • 主线程清空已完成的队列,等待客户端新的请求。

本质上是将主线程 IO 读写的这个操作 独立出来,单独交给一个I/O线程组处理
这样多个 socket 读写可以并行执行,整体效率也就提高了。同时注意 Redis 命令还是主线程串行

这里举个生活中的例子展示IO读写多线程如何提高效率的:

车站买票和上车,买票对应IO读写,上车对应redis操作

假设没有多线程,也就是相当于只有一个买票窗口,那么人太多,买票窗口就会聚集大堆人,一个人买到票才轮到下一个,买票时间随着人数的增多而增多,多线程就是多开几个窗口,解决买票排队情况,降低大家总的买票时间。可能你会问,上车不还是一个一个按顺序上吗,哪怕你全部人一下子买好票了,我上车的速度还是不变啊?对,但上车几乎是不花费时间的(基于内存),也就是不管你现在是1个人买票了,还是1000个人买票了,都可以秒上车,所以买票和上车的瓶颈在买票,提高买票效率则提高整体效率。

3.2.1 写操作支持多线程

可以通过如下参数配置多线程模型:

// 这里说有三个IO 线程,还有一个线程是main线程,main线程负责IO读写和命令执行操作
io‐threads 4 

3.2.2 读操作支持多线程

 // 将支持IO线程执行 读写任务。
io‐threads‐do‐reads yes

3.3. redis6.0之前为什么不支持多线程

  1. 使用 Redis 时,几乎不存在 CPU 成为瓶颈的情况, Redis 主要受限于内存和网络
  2. 使用了单线程后,可维护性高。多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗

3.4 Redis6.0 为什么要引入多线程呢?

  • 可以充分利用服务器CPU的多核资源,而主线程明显只能利用一个
  • 多线程任务可以分摊 Redis 同步 IO 读写负荷,降低耗时

Redis 将所有数据放在内存中,内存的响应时长大约为 100 纳秒,对于小数据包,Redis 服务器可以处理 80,000 到 100,000 QPS,这也是 Redis 处理的极限了,对于 80%的公司来说,单线程的 Redis 已经足够使用了。

随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的 QPS. 常见解决方案是分布式架构中对数据进行分区并采用多个服务器。这种方式: 维护代价大;某些命令失效;数据分区无法解决热点
读/写问题,数据偏斜,重新分配和放大/缩小变得更加复杂等等。

从 Redis 自身角度来说,因为读写网络的 read/write 系统调用占用了 Redis执行期间大部分 CPU 时间,瓶颈主要在于网络的 IO 消耗. redis6.0充分利用多核cpu的能力分摊 Redis 同步 IO 读写负荷


 

作者:乔_帮_主
链接:https://www.jianshu.com/p/de6c41e05f65
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

Reactor 三种模型

Redis的线程模型—文件事件处理器的详解

追求性能极致:Redis6.0的多线程模型

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值