qBittorrent 多任务卡死:NFS sync 引发的 I/O 派对
/ 7 min read
Table of Contents
前言
最近发现 qBittorrent 同时下载四个种子时速度非常慢,上传也会一起受到影响。更麻烦的是 WebUI 会逐渐失去响应,最后连容器都无法正常停止,等满 30 秒后只能被强制终止。
我的媒体目录位于 Synology DS918+,通过 NFS 挂载到 Proxmox VE 中的 Media Apps VM,Plex 和 qBittorrent 共用这个目录。单任务一直没有明显问题,所以最初怀疑的是 qBittorrent 并发设置或网络连接。最后查了一圈,问题却出在 DS918+ 的 NFS 导出仍然使用 sync。
简单来说,四个种子一起写入时,qBittorrent、Linux NFS 客户端和 DS918+ 开始互相等待,最后把 WebUI 也拖死了。
排查过程
为了排除 Peer、Tracker 和公网端口的影响,我先暂停所有下载,直接在同一个 NFS 目录上进行写入测试,并持续请求 qBittorrent WebUI。
单路直接写入约为 21.1 MiB/s,四路并发时合计只剩 3.4 MiB/s。测试期间 WebUI 连续 44 次请求全部超时。此时没有任何 BT 流量,说明只要向 NFS 目录施加写入负载,就可以重现 qBittorrent 卡死。
这组测试使用直接 I/O,并频繁要求数据落盘,因此测到的是最差情况下的同步延迟,而不是 RAID5 的正常顺序写入速度。之后的持续四路写入可以达到 85.6 MiB/s,四块 HDD 的负载也只有约 23%~35%。DS918+ 的 RAID、Btrfs 卷和 Optane 写回缓存均正常,NFS 没有重传或 server not responding,VM 的 CPU、内存和本地磁盘也都没有压力。
所以阵列本身并没有被跑满,问题发生在别的地方。
qBittorrent 在等待什么
停止测试后,WebUI 仍然没有恢复。于是我在重启前检查了 qBittorrent 的线程,抓到了以下调用栈:
folio_wait_writebackfilemap_write_and_wait_rangenfs_wb_allnfs4_file_flushfilp_flushclose其中一个磁盘 I/O 线程卡在 NFS 写回,主线程又在等待这个线程,因此下载、上传、WebUI 和进程退出全部停在了一起。这个现象与 qBittorrent 上游的 NFS lockup 报告基本一致。
当时 qBittorrent 的 Disk I/O type 为 Default。根据 libtorrent 文档,64 位系统上的默认后端通常使用 mmap。mmap 在本地文件系统上未必有问题,但在这条 NFS 路径上,脏页会集中在 unmap 或 close 时回写。实测四路 mmap 只有 45.2 MiB/s,而 POSIX 可以达到 71.7 MiB/s。
继续检查 DS918+ 的实际 NFS 导出,发现参数是:
rw,sync,no_wdelaysync 要求服务端确认数据已经进入稳定存储后再回复客户端。BT 下载会同时产生许多小块、乱序写入,文件关闭时又要排空脏页。单个任务尚且不明显,四个任务一起运行后,WRITE 和 COMMIT 请求便开始在客户端排队。测试中网络往返时间并不高,但 COMMIT 平均完成时间一度达到 2.44 秒。
Linux 的 exports(5) 文档也说明,async 可以在数据稳定落盘前回复客户端,从而提高性能,但服务端异常重启时可能丢失或损坏尚未落盘的数据。
sync 与 async 对比
这个 NFS 目录保存的都是可以重新校验和下载的媒体文件,因此我在 DSM 中启用了异步,然后使用相同参数重新测试。
| 测试 | sync 导出 | async 导出 | 改善 |
|---|---|---|---|
| 单路 POSIX,64 MiB | 61.7 MiB/s | 102.0 MiB/s | +65% |
| 四路 POSIX,4×64 MiB | 71.7 MiB/s | 106.7 MiB/s | +49% |
| 四路 mmap,4×64 MiB | 45.2 MiB/s | 54.8 MiB/s | +21% |
| 四路持续 POSIX | 85.6 MiB/s | 108.5 MiB/s | +27% |
其中变化最明显的是 NFS COMMIT 延迟:
sync: 约 1.18 秒async: 约 0.5 毫秒四路 POSIX 持续写入最终达到 108.5 MiB/s,已经接近 1GbE 的实际极限。NFS 没有出现重传或内核错误,说明之前的瓶颈确实是 sync 带来的稳定写入等待。Synology 的 NFS 性能优化文档也建议高 I/O 场景启用异步,以减少等待磁盘刷新的延迟。
最终配置
最后使用的配置如下:
NFS export: asyncqBittorrent Disk I/O: POSIX-compliantDisk I/O read mode: Enable OS cacheDisk I/O write mode: Enable OS cache客户端仍然使用 NFSv4.1、hard、nconnect=4 和协商出的 128 KiB rsize/wsize。现有参数已经能够跑满 1GbE,所以没有必要继续调整。
qBittorrent 也需要切换到 POSIX。启用 async 后,四路 mmap 仍然只有 54.8 MiB/s,明显低于 POSIX 的 106.7 MiB/s,并且 unmap 时还会阻塞约 1.5~2 秒。NFS 异步解决了服务端的落盘等待,但 mmap 在 NFS 上仍然表现不佳。
需要注意的是,async 并不是可以随便开启的性能选项。它允许服务端在数据真正落盘前返回成功,UPS 可以降低断电风险,但无法避免 DSM 或内核崩溃、硬复位等情况。
对于 torrent 媒体,这个风险可以接受,因为文件能够重新校验和下载。同一台 NAS 上保存照片原图的 NFS 仍然使用 sync,数据库、备份和其他无法重新生成的数据也不建议照搬这套配置。
这次最容易误判的地方,是看到下载速度很低便认为 RAID5 性能不足。实际上磁盘还有大量余量,qBittorrent 却已经被 NFS 的同步提交拖死。如果 qBittorrent 使用 NFS 目录,并且在多任务时连 WebUI 一起卡住,可以先检查 NFS WRITE/COMMIT 延迟和进程的写回调用栈,再考虑调整下载并发或网络参数。