2026-09-12-上传监控与io_uring空转定位.md

上传批次实时监控 + 「CPU 空转」真身定位(io_uring SQPOLL)


一、监控概况(22:30)

指标数值
会话45 个(延续原批次,断点续传)
已收 / 目标66.99GB / 139.7GB = 48.0%(剩余 72.7GB)
实测吞吐2.9 ~ 3.9 MB/s(6 条并发连接)
总体 ETA按当前速率约 5.5 小时
已完成落盘13 个模型(本次会话中尚无新完成,见第二节说明)
服务状态active,RSS 80~85MB 稳定无泄漏,staleUploads=0,无 mergeActive 堆积
日志告警仅 1 条(此前测试产生的僵死连接清理),无事件循环阻塞告警、无 fatal
队列推进150 秒内 10 个文件新进入上传,180 秒内 10 个文件各 +64MB → 确在推进,无死锁

上传行为特征(值得知道)

浏览器仍在跑旧版前端,它把 45 个文件的分块请求一起丢进连接池,Chrome 用 6 条连接 FIFO 轮转:每轮每个文件只推 1 个 64MB 分块。 后果:所有文件齐头并进(都卡在 ~22~26 块),要到队列尾部才会集中完成,中途看不到「一个个完成」。新版前端(刷新后生效)同时只传 3 个文件,会逐个完成,失败也只影响单个文件。

二、重大发现:CPU「空转」的真身是 io_uring SQPOLL(不是连接)

监控中发现进程 单核占用 ~100%(整机 12%),而吞吐只有 3MB/s。逐层定位:

  1. 用户态 vs 内核态:utime 12%,stime 99% → 不是 JS 空转,是内核态。
  2. 存储栈排查:/home 为普通 ext4(/dev/sda5),无加密、无 FUSE、无压缩;排除文件系统开销。
  3. 是否有分块被反复重写:盯住单个 .part 逐秒采样 30 次,大小只增不减、无截断,cancelled_write_bytes 30 秒零增长 → 排除重复/截断重写。
  4. 逐线程归因(决定性):
    2491739 2491748 iou-sqp-2491739  58.7  00:08:57  Ssl

    10 秒窗口内该线程吃掉 545 tick = 54.5% 单核,占进程总 CPU 的 82%,累计已达 约 10 分钟 CPU 时间。 iou-sqp-* = io_uring 的 SQPOLL 忙轮询线程;本机内核 6.6.143-amd64-desktop-hwe,libuv 默认启用了 io_uring 的 SQPOLL 提交队列轮询,文件流式写入期间该线程持续忙等。

  5. A/B 验证:同一 Node 二进制跑异步写测试 ——
    • 默认:进程内出现 iou-sqp-* 线程;
    • UV_USE_IO_URING=0:iou-sqp 线程完全消失(回落线程池)。

与历史故障的对应关系(修正之前的判断)

修复(已装载,待重启生效)

/etc/systemd/system/dl-server.service 增加:

Environment=UV_USE_IO_URING=0
Environment=UV_THREADPOOL_SIZE=8

⚠️ 本次没有重启服务:重启会中断当前 6 条在传分块,你的上传行会报错需重新选择(数据不丢,断点续传)。该配置已在下次重启 / 服务崩溃自动拉起 / 开机时生效。

三、建议

  1. CPU 修复:想立刻生效就说一声,我重启服务(代价:当前上传行需重新选择一次,进度全部保留);否则让它自然在下次重启生效,不影响上传。
  2. 刷新上传页面:可立即获得 ①已完成的 13 个模型秒跳(不再重传)②同时只传 3 个文件、逐个完成 ③单文件失败不拖累其他文件。代价同样是当前行需重新选择。
    • 证据:页面仍在跑旧版 —— 6 条并发上传连接(新版上限 3),且 models/ 下的占位文件 Put VAE here.txt 等被重新写了一遍(新版同名同大小会跳过)。
  3. 吞吐瓶颈 = 无线链路(此处 22:45 更正原判断):
    • 原判断「走有线 enp3s0f1」是错的 —— 那是依据 ip route get(只看路由表对新连接的选路),实际已建立的连接走的是无线 wlp4s0。
    • 实测(22:45):wlp4s0 接收 3.82 MB/s,enp3s0f1 接收 0.02 MB/s;上传连接本端地址为 192.168.31.76(无线 IP)。
    • 根因:下载中心是用 192.168.31.76(无线地址)访问的,所以流量只走无线。
    • 链路质量对比(同一客户端 192.168.31.110):
      出口丢包RTT
      有线 enp3s0f1(192.168.31.114,1000Mb/s)0%1.3ms
      无线 wlp4s0(192.168.31.76)66.7%2925ms
    • TCP 侧印证:rcv_ooopack 高达 1.5 万+(大量乱序包)、delivery_rate 仅 0.83~1.1 Mbps/连接。
    • 建议改用有线地址 http://192.168.31.114:8899(并让客户端走有线),预期提速幅度很大。
    • 附带隐患:本机 两个网卡同处 192.168.31.0/24(有线 192.168.31.114 / 无线 192.168.31.76),属于双归属同网段配置,容易出现 ARP 抖动与路径切换(客户端在有线/无线间漫游时旧连接不释放 → 正是此前「ESTAB 但零吞吐」的成因之一)。
  4. wd-radiance-fp16.safetensors 停在 7/39 块已 4.5 小时(旧批次崩溃时的残留行),需要重新选择该文件才会继续。

四、修复前后 A/B 实测(22:32 重启生效,监控日志天然跨重启)

同一份 40 条采样日志(每 30s 一条)恰好横跨修复重启,构成同源对照:

指标修复前(io_uring 开启)22:16:38~22:32:09修复后(UV_USE_IO_URING=0)22:32:39~22:36:10
吞吐3.21 MB/s3.44 MB/s(+7%)
进程 CPUps %cpu 累计均值 21.3% → 76.6%,瞬时实测 106.7% 单核(内核态占 99%)27% 单核(内核态仅 10%)
iou-sqp 线程1 个(累计 9 分 57 秒 CPU,占进程 CPU 的 82%)0 个
RSS85MB67MB(新进程)
传输量2.92GB0.71GB(窗口更短)

结论:白赚回约 0.73 个核,吞吐不降反升。 压测(640MB 分块上传+合并)1.1 秒完成、约 600MB/s,说明关掉 io_uring 对本场景没有性能损失。

四点五、改走有线之后:瓶颈转移到磁盘(22:57 实测)

用户改用有线地址 http://192.168.31.114:8899 后,网络侧立竿见影:

指标无线(.76)有线(.114)
网卡接收速率3.8~4.2 MB/s(30~35 Mbps)65.5 MB/s(549 Mbps)
上传连接本端地址192.168.31.76192.168.31.114
实测落盘速率3.6~4.0 MB/s40.6 MB/s

网络提速约 16 倍后,新的瓶颈是这台机器的机械硬盘(直接 I/O 绕过页缓存实测,rotational=1):

直接 I/O 测试实测
单路顺序读54.6 MB/s
单路顺序写23.0 MB/s
2 路并发读+写(等价于 2 个合并同时跑)各 10 MB/s,合计约 20 MB/s

原因:合并落盘 = 读 .part(1×)+ 写目标文件(1×),整个流程对磁盘是 3 倍 I/O(上传写 1× + 合并读 1× + 合并写 1×)。实测 2 路合并合计仅 11 MB/s,正好落在这块盘的能力范围内 —— 合并实现没问题,是盘的物理极限。

顺带观察到的流水线现象:客户端要等 complete 响应才释放上传名额,所以合并期间网络空闲(实测接收 0 MB/s、磁盘忙 23.9 MB/s)。由于磁盘已是全局瓶颈,把上传与合并重叠也不会减少总 I/O,因此不能靠调度优化提速,只能减少 I/O 总量。

后续可做的结构性优化(预期 2~3 倍):分块直接写入目标文件的偏移位置(先 ftruncate 出稀疏文件,写完 rename 落盘),彻底省掉「合并拷贝」这一读一写,把 3 倍 I/O 降到 1 倍。对已上传一半的存量会话(.part 已在盘上)收益只有 7%~33%,因此更适合在下一批上传前落地并测试。

五、监控工具

bash ~/Downloads/dl-upload-monitor.sh 30 40    # 每 30s 采样,写入 /tmp/dl-monitor.log
curl -s --noproxy '*' http://127.0.0.1:8899/healthz   # 连接数/上传连接/合并队列/内存

五、相关文件

下载此文件