redis 的线程模型从最刚开始的纯单线程, 到引入异步线程处理耗时操作, 再到最终使用多线程处理 IO 读写操作, 完成了线程模型的全面升级重构;
和普通常见的 IO 密集型应用不同, redis 作为一个内存密集型应用, 使用了绝无仅有的 多线程处理 IO、单线程处理核心逻辑 的模式, 值得我们学习了解, 也为我们在日常工作中, 跳出传统思维框架, 紧贴自身业务特征做最合适的技术选型提供了启发与参考;

单线程模型

redis 单线程模型
redis 单线程模型

借助于 linux epoll I/O 多路复用机制, redis 可以仅凭单线程实现高效的客户端请求处理一条龙服务:

  • 读取数据
  • 命令解析
  • (合并请求)
  • 命令执行
  • 结果写回客户端缓冲区

因为单线程无需考虑锁竞争、线程安全及事务可见性等多线程才会引入的问题, 且不存在线程切换的开销, redis 单线程可以支撑起非常高的单机 qps;

异步线程

redis 存在文件事件时间事件两种事件类型, 对于事件的处理 redis 只在单独的主线程中做, 但是在很多其他逻辑的处理上, 并不是使用单线程:

  • 生成 RDB 文件: fork 一个子进程来实现;
  • big key 删除: 引入了 Lazy Free 机制将其异步化, 主线程只做关系解除以快速返回, 方便继续处理其他事件;

多线程模型

redis 几乎不存在 CPU 或者线程成为瓶颈的情况, 而是主要受限于内存和网络带宽, 即 redis 执行期间大部分 CPU 时间片是用来等待 IO 读写和网络传输;
所以 redis 6.0 为了解决 IO 读写的瓶颈, 引入了多线程以提升 IO 读写性能, 但同时也保留了事件处理的单线程特性, 从而保留了 redis 传统的高效率;

redis 多线程模型
redis 多线程模型

redis 的 IO 线程代替原本的主线程做了如下事情:

  • 读取数据
  • 命令解析
  • (合并请求)
    ……
  • 结果写回

只有最核心的命令执行是交给了主线程去做; 可以看到, 只要不涉及数据状态的操作, 都可以交给 IO 多线程而不用关心线程安全等问题;

多线程特性的启用

redis 6.0 默认是不开启多线程特性的, 如果需要使用多线程功能, 需要在 redis.conf 中完成两个设置:

1
2
3
4
# 启用多线程
io-thread-do-reads yes
# 线程数量
io-threads 6

线程个数要小于 Redis 实例所在机器的 CPU 核个数, 例如对于一个 8 核的机器来说, redis 官方建议配置 6 个 IO 线程;

更高效的实现: Tair RDB 多线程模型

阿里巴巴的缓存产品 Tair RDB 实现了更高效的多线程请求模型:

tair rdb 的线程模型
tair rdb 的线程模型

  • Main Thread: 负责客户端连接建立等;
  • IO Thread: 负责请求读取、响应发送、命令解析等;
  • Worker Thread: 专门用于事件处理、命令执行;

IO Thread 读取用户的请求并进行解析, 之后将解析结果以命令的形式放在队列中发送给 Worker Thread 处理;
Worker Thread 将命令处理完成后生成响应, 通过另一条队列发送给 IO Thread;
为了提高线程的并行度, IO Thread 和 Worker Thread 之间采用无锁队列和管道进行数据交换;

参考资料