|
| 1 | +# Redis 单线程模型 |
| 2 | + |
| 3 | +## Redis 单线程模型 |
| 4 | + |
| 5 | +Redis 基于 Reactor 模式开发了自己的网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。由于文件事件处理器(file event handler)是单线程方式运行的,所以说 Redis 是单线程模型。 |
| 6 | + |
| 7 | +虽然 Redis 是单线程的,但仍然可以监听大量的客户端连接,原因在于文件事件处理器使用 **I/O 多路复用(multiplexing)程序**来同时监听多个客户端连接(即多个套接字),并根据套接字目前执行的任务来为套接字关联不同的事件处理器。也就是说,**I/O 多路复用技术使得 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**。 |
| 8 | + |
| 9 | +### I/O 多路复用 |
| 10 | + |
| 11 | +多路指的是多个 Socke t连接,复用指的是复用一个线程。 |
| 12 | + |
| 13 | +多路复用主要有三种技术:select,poll,epoll。epoll 是最新的也是目前最好的多路复用技术。 |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +### 文件事件处理器 |
| 18 | + |
| 19 | +Redis 服务器是一个事件驱动程序。服务器需要处理两类事件: |
| 20 | + |
| 21 | +- 文件事件:服务器通过套接字与客户端或者其它服务器进行通信,**文件事件就是对套接字操作的抽象**。 |
| 22 | +- 时间事件:服务器有一些操作需要在给定的时间点执行,时间事件是对这类定时操作的抽象。 |
| 23 | + |
| 24 | +文件事件处理器主要是包含 4 个部分: |
| 25 | + |
| 26 | +- 多个 Socket(客户端连接) |
| 27 | +- IO 多路复用程序(支持多个客户端连接) |
| 28 | +- 文件事件分派器(将 Socket 关联到相应的事件处理器) |
| 29 | +- 事件处理器(比如命令请求处理器、命令回复处理器、连接应答处理器) |
| 30 | + |
| 31 | +Redis 选择使用单线程模型处理来自客户端的绝大多数网络请求,主要有以下 3 个原因: |
| 32 | + |
| 33 | +- 几乎不存在 CPU 成为瓶颈的情况, Redis 主要受限于内存和网络。 |
| 34 | + |
| 35 | + Redis 并不是 CPU 密集型的服务,如果不开启 AOF 备份,所有 Redis 的操作都会在内存中完成不会涉及任何的 I/O 操作,这些**数据的读写由于只发生在内存中**,所以处理速度是非常快的; |
| 36 | + |
| 37 | + 整个服务的瓶颈在于**网络传输带来的延迟**和**等待客户端的数据传输**,也就是网络 I/O,所以使用多线程模型处理全部的外部请求可能不是一个好的方案。 |
| 38 | + |
| 39 | +- 使用单线程模型能带来更好的可维护性,方便开发和调试 |
| 40 | + |
| 41 | + 单线程不需要并发控制。 |
| 42 | + |
| 43 | +- 使用单线程模型也能并发的处理客户端的请求 |
| 44 | + |
| 45 | + I/O 多路复用机制。 |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | +## Redis 4.0 后引入多线程 |
| 50 | + |
| 51 | +在 Redis 4.0 之后的版本,Redis 服务在执行一些命令时就会使用 ”主处理线程“ 之外的其他线程,例如 `UNLINK`、`FLUSHALL ASYNC`、`FLUSHDB ASYNC` 等非阻塞的删除操作。 |
| 52 | + |
| 53 | +对于 Redis 中的一些超大键值对,几十 MB 或者几百 MB 的数据并不能在几毫秒的时间内处理完,Redis 可能会需要在释放内存空间上消耗较多的时间。其实释放内存空间的工作可以由后台线程异步进行处理,只需要将键从元数据中删除,而真正的删除操作会在后台异步执行。Redis 引入多线程后,对于一些**大键值对的删除操作**,可以通过**多线程非阻塞地释放内存空间**减少对 Redis 主线程阻塞的时间,提高执行的效率。 |
| 54 | + |
| 55 | +总的来说,Redis 6.0 之前主要还是单线程处理。 |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +## Redis 6.0 引入多线程 |
| 60 | + |
| 61 | +Redis6.0 引入多线程主要是为了提高网络 IO 读写性能。 |
| 62 | + |
| 63 | +虽然 Redis6.0 引入了多线程,但是 Redis 的多线程只是在**网络数据的读写这类耗时操作上使用**,执行命令仍然是单线程顺序执行。所以仍然是线程安全的。 |
| 64 | + |
| 65 | +Redis 6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis.conf 配置文件: |
| 66 | + |
| 67 | +```html |
| 68 | +io-threads-do-reads yes # 开启多线程 |
| 69 | +``` |
| 70 | + |
| 71 | +开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis.conf 配置文件: |
| 72 | + |
| 73 | +```html |
| 74 | +io-threads 2 # 官网建议 4 核的机器建议设置为2或3个线程,8 核的建议设置为6个线程 |
| 75 | +``` |
| 76 | + |
| 77 | +关于线程数,官方有一个建议:4 核的机器建议设置为 2 或 3 个线程,8 核的建议设置为 6个线程,线程数一定要小于机器核数。 |
| 78 | + |
| 79 | +### Redis 6.0 多线程的实现机制 |
| 80 | + |
| 81 | + |
| 82 | + |
| 83 | +### Redis 6.0 多线程和 Memcached 多线程模型对比 |
0 commit comments