aio threads 并非将 i/o 彻底剥离至其他进程,而是让 worker 进程通过内核线程池异步执行 read(),卸下阻塞包袱;需满足内核≥5.1、xfs/ext4 文件系统、nginx 启用 --with-file-aio、禁用 sendfile 且配置 directio 与显式 thread_pool,仅对大文件同步读路径生效。

不能“彻底剥离”——aio threads 不是把 I/O 任务扔给别的进程,而是让当前 worker 进程在发起 read() 时不阻塞事件循环,靠内核线程池异步完成实际读盘,并通过回调通知 Nginx 继续处理。它仍是该 worker 的一部分,只是执行方式从同步等待变为非阻塞调度。
关键不是“剥离到别处”,而是“卸下阻塞包袱”
真正目标是:worker 线程在 top 中 S 态(睡眠等待 I/O)占比持续低于 5%,且不因一次大文件读取而暂停响应新连接。
必须满足的硬性前提
- Linux 内核 ≥ 5.1(低版本仅支持
aio on,且依赖directio) - 文件系统为 XFS 或 ext4(btrfs、NTFS-3G 等不保证稳定)
- Nginx 编译时启用了
--with-file-aio(主流发行版包通常已开启) - 静态文件未被
mmap或sendfile直接绕过(aio对sendfile无效,只对read()路径起作用)
不满足任一条件,aio threads 将静默退化为同步读。
配置要点:三步缺一不可
启用 aio threads
在location块中写:aio threads=disk_io;(不是aio on)强制走 aio 路径
加directio 4m;(例如 4MB 以上文件启用直接 I/O,绕过 page cache,逼出 aio)禁用冲突机制
显式关闭sendfile off;(二者互斥,sendfile优先级更高)
同时建议配合:
-
open_file_cache max=1000 inactive=30s;(减少反复open()开销) -
open_file_cache_valid 60s; open_file_cache_min_uses 2;
线程池必须显式定义并绑定
必须在 main 块中定义线程池,每个 worker 进程独享一个:
thread_pool disk_io threads=32 max_queue=4096;
-
threads=32:按 SSD 随机读吞吐预估(50K–80K IOPS → 推荐 32–128) -
max_queue=4096:必须大于峰值缓存未命中并发量,否则排队等于变相同步
不定义 thread_pool,aio threads=xxx 将无法生效。
它只在特定路径触发,不是“所有读都走”
以下场景才真正启用 aio threads:
-
sendfile off;(禁用内核零拷贝) - 文件大小 ≥
directio设置的阈值(如4m→ ≥4MB) - 非内存映射路径(如非
mmap加载的配置文件) - 典型有效路径:
alias指向的大文件、gzip_static off时读原始.gz、代理模式下缓存未命中读取
小文件(proxy_pass 后端通信等,均不走此路径。
验证是否真解耦:盯两个硬指标
-
top -H -p <worker_pid></worker_pid>中,worker 线程的%S(睡眠态)占比 -
/proc/<pid>/status</pid>查voluntary_ctxt_switches增速平稳,无突增 - 若使用
ngx_http_thread_pool_module,可通过 status 接口观察队列等待时间是否
不看吞吐数字,只盯这两个指标——它们直接反映主线程是否真正“不卡”。











