01-Redis 线程(重要)

官网保平安:https://redis.io/

Redis 线程模型(重要)

对于读写命令来说,Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作, Redis 6.0 版本之后引入了多线程来处理网络请求(提高网络 IO 读写性能)。

Redis 单线程模型了解吗?

Redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。

文件事件处理器的结构包含 4 个部分:多个 socket,I/O 多路复用程序,文件事件分派器,事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)。

多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 I/O 多路复用程序会同时监听多个 socket,将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

如果一个套接字又可读又可写的话,那么服务器将先读套接字,后写套接字。

《Redis 设计与实现》有一段话是如是介绍文件事件处理器的,我觉得写得挺不错。

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。

  • 文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。

文件事件处理器(file event handler)

相关阅读:Redis 事件机制详解

Redis6.0 之前为什么不使用多线程?

虽然说 Redis 是单线程模型,但实际上,Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。

不过,Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主线程之外的其他线程来“异步处理”,从而减少对主线程的影响。

为此,Redis 4.0 之后新增了几个异步命令:

  • UNLINK:可以看作是 DEL 命令的异步版本。
  • FLUSHALL ASYNC:用于清空所有数据库的所有键,不限于当前 SELECT 的数据库。
  • FLUSHDB ASYNC:用于清空当前 SELECT 数据库中的所有键。

redis4.0 more thread

总的来说,直到 Redis 6.0 之前,Redis 的主要操作仍然是单线程处理的。

那 Redis6.0 之前为什么不使用多线程?

官方 FAQ 表示,因为 Redis 是基于内存的操作,CPU 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存的大小或者网络带宽。

既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了(多线程处理会涉及到锁,而且多线程会涉及到线程切换而消耗CPU),Redis 利用队列将并发访问变为串行访问。

相关阅读:为什么 Redis 选择单线程模型?

Redis6.0 之后为何引入了多线程?

Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。

虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要设置 IO 线程数 > 1,需要修改 redis 配置文件 redis.conf

1
io-threads 4 #设置1的话只会开启主线程,官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程

另外:

  • io-threads 的个数一旦设置,不能通过 config 动态设置。
  • 当设置 ssl 后,io-threads 将不工作。

开启多线程后,默认只会使用多线程进行 IO 写入 writes,即发送数据给客户端,如果需要开启多线程 IO 读取 reads,同样需要修改 redis 配置文件 redis.conf :

1
io-threads-do-reads yes

但是官网描述开启多线程读并不能有太大提升,因此一般情况下并不建议开启

相关阅读:

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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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 后台线程有哪些? 这篇就文章。

其它开源软件采用的模型

  • Nginx:多进程单线程模型,每个线程可用 I/O 多路复用。
  • Memcached:单进程多线程模型。

01-Redis 线程(重要)
https://flepeng.github.io/interview-41-数据库-41-Redis-01-Redis-线程(重要)/
作者
Lepeng
发布于
2020年8月8日
许可协议