单线程概念

Redis是单线程的原因

  • Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的

但是,Redis 程序并不是单线程的,Redis 在启动的时候,是会启动后台线程(BIO) 的:

  • Redis 在 2.6 版本,会启动 2 个后台线程处理关闭文件、AOF 刷盘
  • Redis 在 4.0 版本之后新增了一个新的后台线程,用来异步释放 Redis 内存,也就是 lazyfree 线程。
  • 当要删除一个大 key 的时候,不要使用 del 命令删除,del 是在主线程处理的,这样会导致 Redis 主线程卡顿。应该使用 unlink 命令来异步删除大key。
  • Redis 6.0 版本之后,Redis 在启动的时候,默认情况下会额外创建 6 个线程
  • Redis-server : Redis的主线程,主要负责执行命令;
  • 三个后台线程(bio_close_file、bio_aof_fsync、bio_lazy_free)
  • 三个 I/O 线程,用来分担 Redis 网络 I/O 的压力。

Redis的三个后台线程

redis从哪个版本开始是多线程 redis有几个线程_redis从哪个版本开始是多线程

  • BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;
  • BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
  • BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所有对象 / free(skiplist) 释放跳表对象;

Redis单线程模型

redis从哪个版本开始是多线程 redis有几个线程_redis从哪个版本开始是多线程_02


图中的蓝色部分是一个事件循环,是由主线程负责的,可以看到网络 I/O 和命令处理都是单线程。 Redis 初始化的时候,会做下面这几件事情:

  • 首先,调用 epoll_create() 创建一个 epoll 对象和调用 socket() 创建一个服务端 socket
  • 然后,调用 bind() 绑定端口和调用 listen() 监听该 socket;
  • 然后,将调用 epoll_ctl() 将 listen socket 加入到 epoll,同时注册「连接事件」处理函数。

初始化完后,主线程就进入到一个事件循环函数,主要会做以下事情:

  • 首先,先调用处理发送队列函数,看是发送队列里是否有任务,如果有发送任务,则通过 write 函数将客户端发送缓存区里的数据发送出去,如果这一轮数据没有发送完,就会注册写事件处理函数,等待 epoll_wait 发现可写后再处理 。
  • 接着,调用 epoll_wait 函数等待事件的到来:
  • 如果是连接事件到来,则会调用连接事件处理函数,该函数会做这些事情:调用 accpet 获取已连接的 socket -> 调用 epoll_ctl 将已连接的 socket 加入到 epoll -> 注册「读事件」处理函数;
  • 如果是读事件到来,则会调用读事件处理函数,该函数会做这些事情:调用 read 获取客户端发送的数据 -> 解析命令 -> 处理命令 -> 将客户端对象添加到发送队列 -> 将执行结果写到发送缓存区等待发送;
  • 如果是写事件到来,则会调用写事件处理函数,该函数会做这些事情:通过 write 函数将客户端发送缓存区里的数据发送出去,如果这一轮数据没有发送完,就会继续注册写事件处理函数,等待 epoll_wait 发现可写后再处理 。

为什么Redis是单线程还这么快呢

  1. Redis的全部操作是基于内存的。因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU
  2. Redis 采用单线程模型可以避免了多线程之间的竞争(频繁的上下文切换),避免死锁
  3. Redis 采用了 非阻塞I/O 多路复用机制(epoll)处理大量的客户端 Socket 请求

Redis 6.0 之前为什么使用单线程?

  • CPU 并不是制约 Redis 性能表现的瓶颈所在,更多情况下是受到内存大小和网络I/O的限制
  • 使用了单线程后,可维护性高,而使用多线程,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。

Redis 6.0 之后为什么引入了多线程?

  • 在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。
  • Redis引入多线程来处理网络I/O,仍然使用单线程来执行Redis命令。

注意:对于命令的执行,Redis 仍然使用单线程来处理,不要误解 Redis 有多线程同时执行命令。