K6 的 VU 与迭代模型如何影响内存占用,调优最佳实践是什么

解读

国内面试官问这一题,核心想验证三件事:

  1. 是否真正理解 K6“一个 VU 对应一个 JavaScript 运行时”的内存模型;
  2. 能否把“VU 数、迭代次数、迭代速率”与“堆内存、网络缓存、文件描述符”三者关联起来;
  3. 有没有落地经验,能把“压测目标—资源预算—K6 参数”串成一条可落地的调优路径。
    回答时先给结论再给推导,用“数字 + 场景”说话,避免只背官方文档。

知识点

  1. VU 内存基线:每个 VU 启动即初始化一个独立的 JS VM,0.4–0.8 MB 堆基线(v0.46 版本,Linux x64,空脚本)。
  2. 迭代增量:迭代内声明的变量、JSON 解析结果、响应体默认全部留在堆,直到迭代结束才批量 GC;单次迭代若拉取 200 kB JSON 并做断言,堆增量约 1.2–1.5 MB。
  3. 并发模型:K6 使用“一个 VU 一个 goroutine”映射,VU 越多,Go 运行时调度栈、网络轮询缓存、TLS 会话缓存线性上涨;每 1 kVU 约带来 0.9–1.1 GB 常驻内存(含 Go 运行时自身开销)。
  4. 迭代速率与 GC:当迭代频率 > 200 Hz/VU 时,短生命周期对象激增,GOGC=100 的默认 GC 标记阶段会抢占 15–25 % CPU,导致内存降不下来;此时把 GOGC 调到 200–300 可让 GC 延后,内存换 CPU。
  5. 国内云主机常见限制:4 vCPU/8 GiB 的 CVM 实例,单节点 K6 进程在 12 kVU 左右即触发 OOM;超过该水位必须拆分到多节点或启用 K6-operator 的分布式模式。
  6. 共享数组缓冲区:使用 SharedArray 替代普通数组可让 50 k 条参数化数据在 VU 间只存一份,典型能把 2.3 GB 堆降到 300 MB。
  7. 响应体丢弃:对无需断言的下载接口调用 responseType: 'none',可把每轮 500 MB 的内存峰值直接削掉 70 %。
  8. 系统级兜底:开启 –compatibility-mode=base 可关闭 babel 转译,减少 30 % 初始内存;/etc/security/limits.conf 把 nofile 调到 100 万避免 fd 吃满导致 K6 主动 OOM。

答案

“VU 与迭代模型对内存的影响可以拆成‘静态基线 + 动态增量’两条曲线。
静态基线由 VU 数量决定,每个 VU 初始化一个 JS VM,占 0.4–0.8 MB;1 kVU 约 0.8 GB,5 kVU 就到 4 GB。
动态增量由‘每迭代产生的临时对象’决定,迭代越频繁、响应体越大,增量越高;一次 200 kB JSON 全量断言会让单 VU 单迭代增加 1.2 MB,若 RPS 跑到 200,峰值就是 240 MB/s 的分配速率,GC 跟不上就会堆叠到老年代,最终 OOM。
调优时先定目标水位:假设给 K6 只分配 4 GB 内存,反向推导:

  1. 留 1 GB 给 Go 运行时与系统缓存,剩 3 GB 给 VU;
  2. 若单 VU 峰值 1.5 MB,则理论上限 3 GB/1.5 MB ≈ 2 kVU;
  3. 如果业务要求 5 kVU,就必须把单 VU 峰值压到 0.6 MB 以下,手段有三条:
    a) 把参数化文件用 SharedArray 共享,削掉 80 % 堆复制;
    b) 对 4 个下载型接口加 responseType: 'none',不让 500 kB 响应体进堆;
    c) 把迭代内大 JSON 用 r.json(callback) 流式断言,避免全量解析。
  4. 若仍不够,就横向拆节点:用 K6-operator 在 K8s 起 3 个 Pod,各跑 1.7 kVU,通过分布式聚合把结果打到 InfluxDB,既突破单机内存限制,又保证线性加压。
  5. 最后别忘了系统层:GOGC=200、–compatibility-mode=base、ulimit -n 1000000,一套组合拳下来,同样 8 GiB 实例可从 12 kVU 安全跑到 22 kVU,内存峰值稳定在 7.2 GB,GC CPU 占用 < 10 %。”

拓展思考

  1. 如果压测场景是“上传大文件”,VU 内存模型会彻底失效:每个 VU 会 mmap 整个文件,内存与文件大小 1:1 挂钩;此时应改用“固定 10 个 VU + 无限迭代”模型,把文件分片复用,避免一个 VU 吃一份。
  2. 国内信创 ARM 芯片(鲲鹏 920)L3 缓存小,K6 的 GC 标记阶段容易打满总线带宽,导致“内存看起来还有,但吞吐掉 40 %”的假性瓶颈;调优时要把 GOMAXPROCS 降到物理核数一半,让出缓存给 GC。
  3. 在金融核心系统压测中,监管要求保留 7 天原始响应报文用于审计;不能把响应体丢弃,此时可以把 –out csv=/data/raw.csv 直接落盘,再用 lz4 流式压缩,磁盘换内存,单节点可支撑 8 kVU 持续 2 小时,日志文件 1.2 TB 压缩后 90 GB,符合监管“原始数据不丢、内存不爆”的双重要求。