Redis 线程模型
线程模型包括 Redis6.0 之前和 Redis 6.0.
Redis 6.0 前
Redis 是基于 reactor 模式开发了网络事件处理器,这个处理器叫做文件事件处理器(file event handler)。由于这个文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型。采用 IO 多路复用机制同时监听多个 Socket,根据 socket 上的事件来选择对应的事件处理器来处理这个事件。
Redis 为了进行命令的处理,开发了文件事件处理器,包含四个部分:来自多个客户端的套接字、IO 多路复用程序 (异步阻塞 IO)、文件事件分派器和事件处理器。
事件处理器如,连接应答处理,命令请求处理,命令回复处理。连接 redis,连接应答处理;读数据,命令回复处理;读写数据,命令请求处理。

Redis 之前为什么不使用多线程
Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主线程之外的其他线程来“异步处理”,从而减少对主线程的影响。
Redis 4.0 之后新增了几个异步命令:
UNLINK:可以看作是DEL命令的异步版本。FLUSHALL ASYNC:用于清空所有数据库的所有键,不限于当前SELECT的数据库。FLUSHDB ASYNC:用于清空当前SELECT数据库中的所有键。
那 Redis6.0 之前为什么不使用多线程?
- 单线程编程容易并且更容易维护;
- Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
- 根据官网说明,在正常的 linux 系统上,Redis 可以达到每秒百万次的请求处理。
- 内存限制存储数据量,网络传输速度较慢,网络数据协议解析。
- 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
Redis 为什么引入多线程
Redis 4.0,引入多线程处理异步任务,使用多线程来进行删除操作。
Redis 使用多线程来进行删除操作。对于较小的数据,同步地删除消耗不了太多事件;但是对于百十 M 的数据,删除时会消耗一些时间,因此释放内存的工作是由后台线程异步地完成。
Redis 6.0,在网络模型中实现多线程 IO,使用多线程来处理网络数据的读写和协议解析。
Redis 使用多线程来处理网络数据的读写、协议的解析。使用多线程后,可以进一步地减少 Redis 平均处理客户端请求的时间。
Redis 多线程配置
Redis 默认不开启多线程;开启时,多线程线程数要小于机器核数,官方建议大于 8 后没有意义。
Redis 6.0 的多线程默认是禁用的,只使用主线程。如需开启需要设置 IO 线程数 > 1,需要修改 redis 配置文件 redis.conf:
io-threads 4 #设置1的话只会开启主线程,官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程另外:
- io-threads 的个数一旦设置,不能通过 config 动态设置。
- 当设置 ssl 后,io-threads 将不工作。
开启多线程后,默认只会使用多线程进行 IO 写入 writes,即发送数据给客户端,如果需要开启多线程 IO 读取 reads,同样需要修改 redis 配置文件 redis.conf :
io-threads-do-reads yes但是官网描述开启多线程读并不能有太大提升,因此一般情况下并不建议开启。
Redis 多线程是否有线程安全问题
不会,因为多线程进行 xxx,单线程执行。
Redis 后台线程
我们虽然经常说 Redis 是单线程模型(主要逻辑是单线程完成的),但实际还有一些后台线程用于执行一些比较耗时的操作:
- 通过
bio_close_file后台线程来释放 AOF / RDB 等过程中产生的临时文件资源。 - 通过
bio_aof_fsync后台线程调用fsync函数将系统内核缓冲区还未同步到到磁盘的数据强制刷到磁盘( AOF 文件)。 - 通过
bio_lazy_free后台线程释放大对象(已删除)占用的内存空间.
在bio.h 文件中有定义(Redis 6.0 版本,源码地址:https://github.com/redis/redis/blob/6.0/src/bio.h):
#ifndef __BIO_H
#define __BIO_H
/* Exported API */
void bioInit(void);
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);
unsigned long long bioPendingJobsOfType(int type);
unsigned long long bioWaitStepOfType(int type);
time_t bioOlderJobOfType(int type);
void bioKillThreads(void);
/* Background job opcodes */
#define BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */
#define BIO_AOF_FSYNC 1 /* Deferred AOF fsync. */
#define BIO_LAZY_FREE 2 /* Deferred objects freeing. */
#define BIO_NUM_OPS 3
#endif关于 Redis 后台线程的详细介绍可以查看 Redis 6.0 后台线程有哪些? 这篇就文章。