searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Redis 网络模型

2023-03-29 07:15:17
60
0

一、Redis为什么快

主要是因为以下几个特性:

  1. C语言实现
  2. 纯内存操作
  3. IO多路复用
  4. 单线程模型
  5. 数据结构、算法优化

二、选择单线程模型的原因

  1. 避免过多的上下文切换开销
  2. 避免多线程之间同步机制的开销
  3. 简单、可维护

然而,Redis并不是单纯的单线程,即使是Redis 6.0之前,虽然Redis的网络IO部分一直是单线程,但还是有一些任务是通过异步线程去做的,例如删除Key的操作,不同步删除数据,而是先将key从keyspace中移除,然后将删除数据的任务放到一个异步队列里面去,由后台线程去删除,这在删除大key的时候可以有效避免操作阻塞。

6.0之后,引入IO多线程来提升IO的效率,但是,IO线程只是负责命令的解析以及网络IO,具体命令的执行,还是在主线程完成的,维持了Redis任务单线程的特性。

三、Redis多线程网络模型

  • 使用 I/O 线程实现网络 I/O 多线程化,I/O 线程只负责网络 I/O 和命令解析,不执行客户端命令。
  • 利用原子操作+交错访问实现无锁的多线程模型。
  • 通过设置 CPU 亲和性,隔离主进程和其他子进程,让多线程网络模型能发挥最大的性能。

整体流程:

  1. 根据配置,初始化多线程。
  2. 主线程事件循环,将IO事件放到 clients_pending_read中,在beforeSleep方法中,通过RoundRobin的方式将IO事件分发给各个IO线程(包括主线程自己),放到他们的本地队列中;接着主线程会处理自己队列的IO任务,然后忙轮询等待所有IO线程完成任务(io_threads_pending和为0),主线程开始处理任务。
  3. 主线程处理完任务,遍历待写出的 client 队列 clients_pending_write,通过 RR 策略把所有任务分配给 I/O 线程和主线程去将响应数据写回到客户端。
  1. IO线程逻辑:
    1. I/O 线程启动之后,会先进入忙轮询,判断原子计数器中的任务数量,如果是非 0 则表示主线程已经给它分配了任务
    2. 主线程会在每次事件循环中尝试调用 startThreadedIO 唤醒 I/O 线程去执行任务
    3. 遍历自己的本地任务队列 io_threads_list[id],取出一个个 client 执行任务
    1. 全部完成后,计数器置为0(主线程在忙轮询等待)

在整个流程里面,虽然有多线程操作,但是主线程跟IO线程、IO线程之间,并不存在race,而是通过原子操作+交错访问来实现无锁。

0条评论
0 / 1000
冯****燕
3文章数
0粉丝数
冯****燕
3 文章 | 0 粉丝
冯****燕
3文章数
0粉丝数
冯****燕
3 文章 | 0 粉丝
原创

Redis 网络模型

2023-03-29 07:15:17
60
0

一、Redis为什么快

主要是因为以下几个特性:

  1. C语言实现
  2. 纯内存操作
  3. IO多路复用
  4. 单线程模型
  5. 数据结构、算法优化

二、选择单线程模型的原因

  1. 避免过多的上下文切换开销
  2. 避免多线程之间同步机制的开销
  3. 简单、可维护

然而,Redis并不是单纯的单线程,即使是Redis 6.0之前,虽然Redis的网络IO部分一直是单线程,但还是有一些任务是通过异步线程去做的,例如删除Key的操作,不同步删除数据,而是先将key从keyspace中移除,然后将删除数据的任务放到一个异步队列里面去,由后台线程去删除,这在删除大key的时候可以有效避免操作阻塞。

6.0之后,引入IO多线程来提升IO的效率,但是,IO线程只是负责命令的解析以及网络IO,具体命令的执行,还是在主线程完成的,维持了Redis任务单线程的特性。

三、Redis多线程网络模型

  • 使用 I/O 线程实现网络 I/O 多线程化,I/O 线程只负责网络 I/O 和命令解析,不执行客户端命令。
  • 利用原子操作+交错访问实现无锁的多线程模型。
  • 通过设置 CPU 亲和性,隔离主进程和其他子进程,让多线程网络模型能发挥最大的性能。

整体流程:

  1. 根据配置,初始化多线程。
  2. 主线程事件循环,将IO事件放到 clients_pending_read中,在beforeSleep方法中,通过RoundRobin的方式将IO事件分发给各个IO线程(包括主线程自己),放到他们的本地队列中;接着主线程会处理自己队列的IO任务,然后忙轮询等待所有IO线程完成任务(io_threads_pending和为0),主线程开始处理任务。
  3. 主线程处理完任务,遍历待写出的 client 队列 clients_pending_write,通过 RR 策略把所有任务分配给 I/O 线程和主线程去将响应数据写回到客户端。
  1. IO线程逻辑:
    1. I/O 线程启动之后,会先进入忙轮询,判断原子计数器中的任务数量,如果是非 0 则表示主线程已经给它分配了任务
    2. 主线程会在每次事件循环中尝试调用 startThreadedIO 唤醒 I/O 线程去执行任务
    3. 遍历自己的本地任务队列 io_threads_list[id],取出一个个 client 执行任务
    1. 全部完成后,计数器置为0(主线程在忙轮询等待)

在整个流程里面,虽然有多线程操作,但是主线程跟IO线程、IO线程之间,并不存在race,而是通过原子操作+交错访问来实现无锁。

文章来自个人专栏
学习笔记
3 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0