
本文介绍在 k6 多虚拟用户(vu)压测场景下,如何确保每个 vu 从列表 api 获取互不重叠的 id 子集,从而避免并发调用删除等幂等性敏感接口时出现“重复删除”错误。核心方案是基于 vu 编号对 id 列表做模运算分片。
本文介绍在 k6 多虚拟用户(vu)压测场景下,如何确保每个 vu 从列表 api 获取互不重叠的 id 子集,从而避免并发调用删除等幂等性敏感接口时出现“重复删除”错误。核心方案是基于 vu 编号对 id 列表做模运算分片。
在 k6 分布式压测中,多个 VU 并发执行相同逻辑(如登录 → 拉取 ID 列表 → 编辑 → 删除)时,若所有 VU 均遍历同一份 ID 列表,极易引发资源竞争:例如某 ID 被 VU-1 删除后,VU-2 和 VU-3 仍可能尝试再次删除,导致 404 或 409 错误——这并非目标业务异常,而是测试设计缺陷。
根本解决思路:让每个 VU 只处理其专属子集。推荐采用「静态分片 + VU 标识路由」策略,即在 setup() 阶段一次性获取完整 ID 列表,并按 id % options.vus === __VU - 1 规则为每个 VU 分配唯一 ID(注意:k6 中 __VU 从 1 开始编号,而模运算通常以 0 为基,故需 -1 对齐)。
以下为可直接运行的示例代码:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 3,
duration: '30s',
};
export function setup() {
// 1. 登录并获取 token(略去具体实现)
const loginRes = http.post('https://api.example.com/login', JSON.stringify({ user: 'test', pass: '123' }));
const token = loginRes.json('token');
// 2. 调用列表 API 获取全部 ID(建议限制总量,避免内存溢出)
const listRes = http.get('https://api.example.com/items', {
headers: { Authorization: `Bearer ${token}` }
});
const allIds = listRes.json().map(item => item.id); // 假设响应为 [{id: 1}, {id: 2}, ...]
return { token, allIds };
}
export default function (data) {
const { token, allIds } = data;
// 3. 每个 VU 仅选取属于自己的 ID(关键逻辑!)
const myIds = allIds.filter((id, idx) => id % __ENV.VUS === __VU - 1);
// 更健壮写法(兼容非连续 ID):allIds.filter(id => id % options.vus === __VU - 1)
if (myIds.length === 0) {
console.warn(`VU ${__VU} has no assigned IDs — skipping operations`);
return;
}
// 4. 对分配到的 ID 执行编辑 & 删除(按需添加错误重试或跳过逻辑)
myIds.forEach(id => {
// 编辑
const editRes = http.put(`https://api.example.com/items/${id}`, JSON.stringify({ content: 'updated' }), {
headers: { Authorization: `Bearer ${token}` }
});
check(editRes, { 'edit success': (r) => r.status === 200 });
// 删除
const deleteRes = http.del(`https://api.example.com/items/${id}`, null, {
headers: { Authorization: `Bearer ${token}` }
});
check(deleteRes, { 'delete success': (r) => r.status === 204 || r.status === 200 });
sleep(0.5); // 避免请求过于密集
});
}
✅ 关键注意事项:
-
setup()返回的数据在各 VU 间共享只读,因此allIds是安全的全局数据源; -
__VU是当前 VU 序号(1~N),options.vus是总 VU 数,二者必须严格匹配(推荐使用__ENV.VUS或显式传参避免歧义); - 若原始 ID 非整数(如 UUID),需先哈希转为数字再取模,例如:
Math.abs(hashString(id)) % options.vus; - 生产级压测建议增加 fallback 机制:当某 VU 分配 ID 过少时,可动态合并相邻 VU 的余量,或改用
SharedArray+ 原子计数器实现动态负载均衡。
通过该方案,3 个 VU 将自动划分 ID 空间(如 ID=1,4,7…→VU1;2,5,8…→VU2;3,6,9…→VU3),彻底消除跨 VU 冲突,使压测结果真实反映系统吞吐与稳定性。










