Redis之IO多路复用 select,poll,epoll
简单来说,IO 多路复用的核心思想是 “一个线程盯多个 IO 事件”,而不是为每个 IO 请求创建一个线程。这样能极大减少线程切换的开销,让 Redis 在单线程模型下也能支撑数万级别的并发连接。
一、Redis 的 IO 多路复用模型
Redis 的 IO 多路复用模型工作流程可拆解为 3 个步骤:
- 注册事件:Redis 将所有客户端的 Socket 连接,以及需要监听的 IO 事件(如 “读事件”“写事件”),注册到内核提供的 select/poll/epoll 函数中。
- 阻塞等待:Redis 主线程调用这些函数后会阻塞,直到内核通知有至少一个 IO 事件就绪(比如某个客户端发送了数据,对应的 Socket 可读)。
- 处理事件:内核返回就绪的 IO 事件列表,Redis 主线程遍历该列表,逐一处理每个就绪事件(如读取客户端数据、执行命令、返回结果),处理完后再次回到阻塞等待状态。
二、select、poll、epoll 的核心区别
这三个函数都是内核提供的 IO 多路复用工具,但在性能、容量、效率上有显著差异,Redis 会根据操作系统自动选择最优方案(优先 epoll)。
| 对比维度 | select | poll | epoll |
|---|---|---|---|
| 文件描述符上限 | 有上限(默认 1024),需修改内核参数 | 无上限,但需遍历所有注册的 FD | 无上限,且无需遍历所有 FD |
| 效率 | 低,需遍历所有注册的 FD 判断是否就绪 | 低,同 select,仅数据结构不同 | 高,内核直接返回就绪的 FD 列表 |
| 数据结构 | 基于数组 | 基于链表 | 基于红黑树 + 就绪链表 |
| 触发方式 | 仅水平触发(LT) | 仅水平触发(LT) | 支持水平触发(LT)和边缘触发(ET) |
关键概念补充:
- 水平触发(LT):只要 Socket 有数据可读 / 可写,内核就会持续通知。即使本次没处理完,下次调用函数仍会提醒,兼容性好但效率稍低。
- 边缘触发(ET):仅在 Socket 状态从 “未就绪” 变为 “就绪” 时通知一次。需一次性处理完所有数据,效率更高,但编程复杂度高。
三、为何 Redis 优先选择 epoll?
在 Linux 系统下,Redis 默认使用 epoll 而非 select 或 poll,核心原因有 3 点:
- 无连接数上限:epoll 不依赖数组或链表,能支持远超 1024 的客户端连接,满足 Redis 高并发场景需求。
- 更高效率:epoll 无需遍历所有注册的连接,只需处理内核返回的 “就绪连接列表”,在连接数多但活跃连接少的场景下,效率优势极其明显。
- 支持边缘触发:epoll 的 ET 模式能减少内核通知的次数,进一步降低 CPU 开销,让 Redis 主线程更专注于处理业务逻辑。
