Redis 的主线程不想等任何慢的系统调用,所以把其中几类交给后台线程(BIO):AOF 的 fsync、大对象的内存释放、文件的关闭/删除。这篇不数线程,而是用行为来验证:制造磁盘拥塞,看 fsync 在主线程上和在后台线程上的延迟有多大差别;再看后台释放大对象时的计数器。
appendfsync 磁盘负载 条数 p50 ms p99 ms p99.9 ms max ms aof_delayed_fsync everysec 无 274496 0.028 0.043 0.08 1.9 0 everysec 有 294232 0.027 0.043 0.08 14.2 0 always 无 2800 2.989 4.607 9.20 10.8 0 always 有 2064 2.591 14.118 32.78 288.8 0
appendfsync everysec 下,fsync 由后台线程每秒做一次:磁盘拥塞时请求延迟的 p50/p99 几乎不变(p99 0.043 → 0.043 ms),最大值有偶发的毫秒级尖峰(14.2 ms)。appendfsync always 下,fsync 在请求路径上:无负载已经是 p50 3.0 ms,磁盘拥塞时 p99 14.1 ms、最大 289 ms。(磁盘拥塞是我用另一个进程的持续 fsync 制造的,macOS + SSD;不同磁盘上的绝对数字会不同。)
everysec 也不是永远安全:按文档的说法,如果一次 fsync 耗时过长(约 2 秒),主线程会被迫等待并增加 aof_delayed_fsync——这次实验里它始终是 0,没有触发,所以这一点没有验证。
UNLINK 之后每 ~20 ms 采样 (lazyfree_pending_objects, lazyfreed_objects): [('1', '0'), ('1', '0'), ('1', '0'), ('1', '0'), ('1', '0'), ('0', '1')]UNLINK 一个 200 万成员的集合:主线程只把 key 摘下并交给后台线程,INFO memory 的 lazyfree_pending_objects 显示 1 个待释放对象,大约 100 ms 后归 0、lazyfreed_objects 加 1。lazy free 各开关的效果见第 108 篇。
BIO 还有一类任务是「关闭/删除大文件」(比如 AOF 重写完成后删除旧文件),我没有找到办法在这台机器上单独观察它;线程的名字与具体数量在 macOS 上也看不到(第 106 篇只数出了线程总数)。所以这里说的三类任务里,fsync 与 lazy free 是我用行为验证的,文件关闭是按 Redis 文档转述的。