Redis线程模型

1.1 总结

Redis 整体是多线程的,至于平时所说的 Redis 单线程是指 Redis的网络io 和 键值对读写 是在同一个线程中完成

1.2 一次 Redis 请求过程

  1. 监听客户端请求(bind/listen
  2. 等待客户端的连接(accept
  3. socket 中读取数据(recv
  4. 解析客户端发送的内容(parse
  5. 根据解析出来的内容进行内存操作(get/set
  6. 执行完成命令之后给客户端响应结果,即向 socket 中写数据(send
graph TB subgraph A[redis线程] subgraph B[网络IO处理] B1[bind/listen] B2[accept] B3[recv] B4[parse] end subgraph C[键值对读写] C1[get/set] end subgraph D[网络IO处理] D1[send] end end B-->C-->D
  • 网络 io 处理过程中, listenrecv 这两个步骤是可能阻塞的,即当没有连接或客户端没有发送命令时,服务器处理这两个环节时会阻塞,但是socket网络模型自身就支持非阻塞的形式 对于多个客户端的请求流程如下图所示

1.3 Redis4 版本中的多线程

  1. Redis4 中主要是增加了异步删除 机制,比如 UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC 等非阻塞的删除操作。
  2. 引入异步删除的原因是现在服务器的内存原来越大,Redis 中可能会存在 bigKey, 对于这些 bigKey 的删除如果使用阻塞的方式,会导致服务器的响应时间变长,所以引入了异步删除的机制,即当删除 bigKey 的时候,会将删除操作放入一个异步队列中,由一个单独的线程来负责异步删除,这样就可以提高服务器的响应时间

1.4 Redis6 版本的多线程

redis6 中有几个重要特性,比如

  1. 面向网络处理 的多 IO 线程
  2. 客户端缓存
  3. 细粒度的权限控制

本节主要讲解 Redis6 中的多线程模型,在 Redis6 之前,从网络io处理到键值对读写都是在同一个线程中完成的 , 随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络io的处理上,即 单个主线程处理网络请求的速度跟不上底层网络硬件的速度 ,所以在 Redis6 中使用多个网络io线程来处理网络请求,提高网络请求处理并行的能力, 需要注意的是 Redis6的多线程模型只是针对网络io部分,Redis的数据读写依然是在一个线程中完成的

1.4.1 Redis6中主线程和io线程协作流程

graph TB subgraph A["redis主线程(接收请求)"] direction TB A1["接收建立连接请求,获取socket"] A2["将socket放入全局等待队列"] A3["以轮询方式将socket连接分配给IO线程, 主线程开始阻塞-----"] A1-->A2-->A3 end subgraph B["redis网络IO线程(解析请求)"] direction TB B1["将socket和线程绑定"] B2["读取socket中的请求并解析"] B3["请求解析完成"] B1-->B2-->B3 end subgraph BB["redis网络IO线程(解析请求)"] direction TB BB1["将socket和线程绑定"] BB2["读取socket中的请求并解析"] BB3["请求解析完成"] BB1-->BB2-->BB3 end A--"io线程开始执行"-->B & BB subgraph C["redis主线程(命令读写)"] direction TB C1["执行请求的命令操作"] C2["请求的命令操作执行完成"] C3["将结果数据写入缓冲区"] C4["等待io线程完成数据回写socket,主线程阻塞---"] C1-->C2-->C3-->C4 end B-->C BB-->C subgraph D["redis网络IO线程(结果响应)"] direction TB D1["将结果数据回写socket(这一步是并行处理)"] D2["socket回写完成"] D1-->D2 end subgraph DD["redis网络IO线程(结果响应)"] direction TB DD1["将结果数据回写socket(这一步是并行处理)"] DD2["socket回写完成"] DD1-->DD2 end C-->D & DD D-->E["主线程清空等待队列,等待后续请求"] DD-->E["主线程清空等待队列,等待后续请求"]

1.4.2 阶段1

  1. 服务端和客户端连接 socket 连接,并将 socket 放入全局等待队列
  2. 主线程通过轮询的方式,将 socket 连接分配给 io 线程
  3. 主线程进入阻塞状态

1.4.3 阶段2

  1. io 线程完成客户端请求读取和解析
  2. io 线程有多个,是并发操作的, 所以会很快

1.4.4 阶段3

  1. 等待 io x线程将命令解析完成后,主线程会以单线程的方式执行这个命令操作

1.4.5 阶段4

  1. 主线程执行完请求操作后,将需要返回的结果写入缓冲区,然会,主线程会阻塞等待 io 线程把这些结果写回到 socket 中,并返回给客户端
  2. io 线程回写 socket 之后,主线程会清空全局队列,等待客户端的后续请求

1.5 Redis6 多线程模型的启用

  1. Redis6 中,多线程模块默认是关闭的
  2. 可以在 redis.conf 中设置 io-threads-do-reads yes 来开启多线程
  3. 可以在 redis.conf 中设置线程个数,比如 io-threads 4, 一般建议线程个数小于 redis 实例所在机器的 cpu 核个数

1.6 疑问解答

  1. Redis6.0增加了IO线程来处理网络请求,如果客户端先发送了一个set key1 val1写命令,紧接着发送一个get key1读命令。由于IO线程是多线程处理的,是否会导致get key1读命令 先于 set key1 val1写命令执行呢?结果客户端读到了key1的旧值的情况发生?

如果这两条命令是同一个客户端发送的,有明显的先后顺序,就不会出现这种情况,因为接收请求的还是主线程,主线程接收请求后会将命令入队列,这个是可以保证顺序的,虽然后续是交给多线程来解析请求,但是命令读写的顺序还是会按照队列中命令先后出现的顺序,而且命令读写是通过主线程单线程操作的。

如果这里说的是多个客户端发送的命令,那就需要看哪个客户端发送的请求先到达服务端,先到达的命令会先执行

相关推荐
ABin-阿斌17 分钟前
SpringBoot 整合 Easy_Trans 实现翻译的具体介绍
java·spring boot·后端
终末圆32 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
_.Switch37 分钟前
Python Web 架构设计与性能优化
开发语言·前端·数据库·后端·python·架构·log4j
2401_8576009542 分钟前
心理教育辅导系统的设计与Spring Boot实现
java·spring boot·后端
北飞的山羊1 小时前
【计算机网络】详解UDP套接字&网络字节序&IP地址&端口号
linux·服务器·网络·后端·计算机网络·udp·信息与通信
☼YJLH☾1 小时前
第十章,XML
xml·java·后端·intellij-idea
拜见老天師1 小时前
SpringBoot中对数据库连接配置信息进行加密处理
数据库·spring boot·后端
記億揺晃着的那天2 小时前
SpringCloud从零开始简单搭建 - JDK17
java·spring boot·后端·spring cloud·nacos
憨憨憨憨憨到不行的程序员2 小时前
Spring框架基础知识
java·后端·spring
Adolf_19932 小时前
Flask-SQLAlchemy一对多 一对一 多对多关联
后端·python·flask