「Redis 是单线程的」这句话对也不对:命令确实是一个线程在执行,但进程里有好几个线程。这篇数一数线程,再用真正的压测工具把「单线程执行」的后果量出来:吞吐的上限在哪里、CPU 长什么样。
默认 线程数 = 4(含主线程) io-threads 4 线程数 = 7(含主线程) io-threads 4 + appendonly yes 线程数 = 7(含主线程)
默认配置下进程有 4 个线程:主线程负责接收请求、执行命令、回复;另外 3 个是后台线程(第 107 篇看它们做什么)。io-threads 4 增加了 3 个 I/O 线程,总共 7 个(第 109 篇)。线程数取自 ps -M 与 top 的输出,macOS 上没法直接看线程名,所以「哪个是 BIO」这里没有验证。
连接数 吞吐(条/秒)
1 508219
4 1168454
16 1340569
64 13502381 个连接 508k 条/秒,4 个连接 1168k,16 个连接 1341k,64 个连接 1350k。前面是客户端不够快,加连接就涨;16 个连接之后到顶——服务器处理不过来了。(我先用 Python 线程做过一次类似实验,结果被 GIL 限制成客户端瓶颈,没有意义,换成了 C 写的 redis-benchmark。)
top 三次采样(pid %cpu 线程数): ['30145 0.0 4/1', '30145 71.2 4/1', '30145 99.9 4/1', '30145 99.5 4/1']
压测时 top 三次采样,Redis 进程的 CPU 到 99.9%(第一次采样在压测刚开始,数值低),线程数 4,同一时刻只有 1 个线程在运行。这是主线程跑满一个核。
命令是串行执行的:一个慢命令占着主线程,所有客户端都排在后面(第 96 篇的排队实验、第 81 篇的 MIGRATE 阻塞)。单实例的吞吐上限就是这个平台,想更高只能多实例(分片/集群,第 68 篇)或开启 I/O 多线程分担网络读写(第 109 篇)。