Redis是如何实现IO多路复用的?

Redis 通过使用操作系统提供的 IO 多路复用​ 机制来实现高性能的网络通信,使得单个线程能够同时处理多个客户端连接。以下是其核心实现方式:


1. 支持的 IO 多路复用模型

Redis 在运行时自动选择系统最高效的多路复用模型(按优先级):

  • epoll(Linux)
  • kqueue(FreeBSD/macOS)
  • select(跨平台,但效率较低)

Redis 在 ae.c文件中抽象了事件驱动模型,通过条件编译适配不同平台。


2. 核心抽象:aeEventLoop

Redis 定义了事件循环结构体 aeEventLoop,其中关键字段包括:

  • 事件处理器数组(用于存储文件事件)
  • 就绪事件队列
  • 底层多路复用 API 的函数指针(如 epoll_waitkevent

3. 事件处理流程

步骤 1:注册事件

  1. 客户端连接到 Redis 时,服务器会为对应的 socket 注册可读事件,并绑定回调函数(如 readQueryFromClient)。
  2. 当需要向客户端回复数据但 socket 暂时不可写时,注册可写事件,并绑定写回调函数。

步骤 2:监听就绪事件

  • 调用 aeApiPoll(底层可能是 epoll_wait/kevent/select)监听所有被注册的 socket。
  • 当某个 socket 可读/可写时,多路复用器返回就绪事件列表。

步骤 3:处理事件

Redis 按顺序处理就绪事件,执行对应的读写回调函数。


4. 底层多路复用实现示例(以 epoll 为例)

Redis 在 Linux 下使用 epoll 的简化流程:

// 初始化 epoll
state->epfd = epoll_create(1024);

// 注册事件(如监听 socket 可读事件)
struct epoll_event ee;
ee.events = EPOLLIN;  // 监听可读事件
ee.data.fd = fd;
epoll_ctl(state->epfd, EPOLL_CTL_ADD, fd, &ee);

// 事件循环
numevents = epoll_wait(state->epfd, events, AE_SETSIZE, timeout);
for (j = 0; j < numevents; j++) {
    // 处理可读/可写事件
    if (events[j].events & EPOLLIN) {
        readProc(events[j].data.fd, ...);
    }
    if (events[j].events & EPOLLOUT) {
        writeProc(events[j].data.fd, ...);
    }
}

5. 为什么单线程还能高效?

  • 非阻塞 IO:所有 socket 设置为非阻塞模式,读写不会阻塞线程。
  • 事件驱动:只有活跃的 socket 会触发回调,避免了轮询所有连接的开销。
  • 纯内存操作:数据操作在内存中完成,极快,不会阻塞网络 IO。

6. Redis 6.0 之后的优化

Redis 6.0 引入了多线程 IO(Threaded I/O),但仅用于处理网络数据的读写和解析,命令执行仍是单线程。这进一步提升了吞吐量,但核心的事件循环模型不变。


总结

Redis 的 IO 多路复用通过以下方式实现:

  1. 使用操作系统提供的 epoll/kqueue/select监听大量 socket。
  2. 事件驱动模型,只有活跃连接会触发回调。
  3. 单线程处理命令,避免了锁和上下文切换开销,同时通过非阻塞 IO 和事件循环实现高并发。

这种设计让 Redis 能以极低的延迟同时处理数万甚至数十万的并发连接。


作 者:南烛
链 接:https://www.itnotes.top/archives/1046
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇