基准测试工具之你不知道的两三事 | 6.如何模拟线上读写混合负载

基准测试工具之你不知道的两三事 | 6.如何模拟线上读写混合负载

上一篇文章中,我们介绍了读写混合负载为何不能只看纯写或纯读结果;在此之前,我们已经跑通了写入测试,并把线上设备的上报节奏、批写、乱序和峰值翻译成了写入负载。

但真实系统往往不会在写入时完全停止读取。设备持续上报的同时,仪表盘要刷新最新状态,值班人员会查看一段时间内的趋势,告警服务也可能触发聚合统计。此时,只测纯写入已经不足以回答“线上运行是否流畅”。

这一篇继续使用 IoT Benchmark,讨论怎样从纯写基线出发,逐步构造一份可解释的读写混合负载。

在这里插入图片描述

1. 先想清楚:写入时,谁在读什么?

读写混合负载的难点不在于“加几个查询”,而在于读请求本身可能完全不同。查看设备最新状态、展示过去一小时曲线、统计当天平均值,虽然都叫“查询”,但对数据库施加的压力并不一样。

在配置前,先用业务语言回答下面三个问题:

  1. 谁在读:是少量运维人员、固定仪表盘,还是大量终端用户?
  2. 读什么:是一个设备的最新点、一个时间范围,还是带聚合的统计结果?
  3. 读多频繁:是每秒刷新,还是告警触发后偶尔回溯?

只有这三个问题有了答案,读操作比例和查询时间范围才有明确的来路。

图中的三类读请求不必同时出现。一次测试只应保留业务中确实存在、且当前希望评估的那几类操作。

2. 用 OPERATION_PROPORTION 组织操作比例

IoT Benchmark 的常规模式 testWithDefaultPath 支持读写混合。核心配置项是 OPERATION_PROPORTION:它用一串以英文冒号分隔的整数,定义写入与各类查询操作的相对比例。

当前默认配置的顺序为:

1
写入 : Q1 : Q2 : Q3 : Q4 : Q5 : Q6 : Q7 : Q8 : Q9 : Q10 : Q11 : Q12

其中,Q1 是精确点查询,Q2 是按时间范围查询,Q4 是带时间条件的聚合查询,Q8 是最近点查询;其余位置对应带值过滤、分组、倒序或集合操作等查询。具体查询类型和支持范围应以当前 config.properties 及目标数据库模块的说明为准。

例如,下面的配置并不是性能推荐值,而是表达一种业务假设:四份写入操作对应一份精确点查询、一份时间聚合查询和一份最近点查询。

1
2
3
4
BENCHMARK_WORK_MODE=testWithDefaultPath

# 写入 : Q1 : Q2 : Q3 : Q4 : Q5 : Q6 : Q7 : Q8 : Q9 : Q10 : Q11 : Q12
OPERATION_PROPORTION=4:1:0:0:1:0:0:0:1:0:0:0:0

比例表达的是相对构成,不是“每秒必须执行几次”。真正的到达节奏还会受到客户端数量、批写大小和操作间隔等设置影响。因此,在文章或报告中应同时记录比例、并发和时间参数,而不能只写一串比例数字。

3. 十三种操作分别在模拟什么?

OPERATION_PROPORTION 中的每一个位置都对应一种明确操作。写入操作和 Q1-Q12 加起来一共 13 项;不需要全部启用,但应知道每一项在模拟什么,才能正确选择比例。

位置 操作 操作含义 更贴近的线上场景
第 1 项 写入 向数据库写入生成的时序数据 设备、网关或应用持续上报。
Q1 精确点查询 查询指定设备在某个时间点的数据 点击某时刻的数据点,或核对单条采样。
Q2 时间范围查询 只按起止时间过滤的范围查询 展示某设备最近一小时或一天的趋势。
Q3 带值过滤的范围查询 在时间范围内再按数值条件过滤 查找温度超过阈值的历史记录。
Q4 带时间条件的聚合查询 在一个时间范围内计算聚合结果 统计某时段的平均值、最大值或计数。
Q5 带值过滤的聚合查询 对满足数值条件的数据进行聚合 统计超过阈值的数据量。
Q6 带时间和值过滤的聚合查询 同时限制时间范围和数值条件后聚合 统计某个班次内的异常指标。
Q7 分组聚合查询 按时间窗口分组后聚合 把原始数据降采样后绘制趋势图。
Q8 最近点查询 获取某设备最新的时间和值 实时状态卡片、设备在线概览。
Q9 倒序范围查询 按时间倒序返回一个时间范围的数据 页面优先展示最新的历史事件。
Q10 倒序带值过滤的范围查询 倒序返回满足数值条件的数据 优先查看最近发生的异常记录。
Q11 倒序分组聚合查询 按时间窗口聚合后倒序返回 从最新窗口开始查看统计趋势。
Q12 集合操作查询 将多个范围查询用 union 等集合操作组合 对多个条件或多个时间窗口的结果做集合运算。

当前配置注释还标出了部分限制:Q11 目前仅支持 IoTDB、TDengine 3.0 和 InfluxDB v1;Q12 当前仅支持 IoTDB 2.0 的表模型。其他操作是否支持,也应在测试前查看目标数据库模块的说明。

初次构造混合负载时,通常优先从 Q8、Q2 和 Q4 中选择:它们分别覆盖“看最新状态”“看一段趋势”和“看一段统计”三类常见需求。Q3、Q5、Q6、Q9-Q12 更适合业务确实有阈值筛选、倒序浏览或集合计算时再单独加入。

例如,下面两组比例都只表达场景,不代表通用推荐值:

1
2
3
4
5
# 设备状态页:8 份写入,对应 1 份最近点查询(Q8)
OPERATION_PROPORTION=8:0:0:0:0:0:0:0:1:0:0:0:0

# 趋势页面:8 份写入,对应 1 份时间范围查询(Q2)
OPERATION_PROPORTION=8:0:1:0:0:0:0:0:0:0:0:0:0

如果业务同时既要看状态又要看趋势,不要马上叠加所有操作。建议先分别运行“写入 + Q8”和“写入 + Q2”,确认每类查询的影响后,再设计包含两者的组合负载。

4. 新近数据与历史数据,要分开设计

线上仪表盘常常关心刚刚写入的数据。IoT Benchmark 提供了 IS_RECENT_QUERY 参数:在读写混合场景下开启后,查询会优先落在最近写入数据附近。

1
2
# 在读写混合场景下,优先查询最近写入的数据
IS_RECENT_QUERY=true

这适合模拟“设备正在上报,页面同时刷新”的场景。但它不等于所有业务查询都应该读取最新数据。历史趋势分析、班次报表或事故回溯,往往会访问更早的时间范围;若把它们都混进“最近数据”测试,结果反而失去业务含义。

可以按查询目标把场景分成两类:

线上行为 更贴近的查询设计 需要重点观察
设备状态卡片持续刷新 最近点查询或最近数据附近的查询 写入是否仍稳定,最新读是否出现明显尾延迟。
趋势图展示一段时间曲线 时间范围查询 查询范围增大后,是否影响写入吞吐和 P99。
告警后查看统计摘要 带时间条件的聚合查询 聚合请求出现时,写入和查询是否都保持可接受延迟。
离线报表或历史回溯 单独设计历史读负载 不要把低频重查询误当成实时面板请求。

QUERY_DEVICE_NUM、QUERY_SENSOR_NUM、QUERY_INTERVAL 等参数用来继续描述查询涉及多少设备、多少测点和多长的时间范围。它们应来自页面实际一次会展示多少设备和多长窗口,而不是为了“把查询压大”随意填写。

5. 从纯写基线,逐步加入查询

读写混合最稳妥的设计方式,不是从一开始就同时开启多种查询,而是把纯写负载作为基线,再逐步加入一类读操作。

例如,可以先保持写入的设备数、客户端数、批大小和乱序比例不变,只把 OPERATION_PROPORTION 从纯写改为“写入 + 最近点查询”。确认这一步的写入吞吐、写入 P99、查询 P99 和失败数后,再考虑加入时间范围或聚合查询。

这样的顺序有两个好处:第一,性能变化更容易归因;第二,若某个查询类型并不被目标数据库模块支持,可以尽早发现,而不会把“功能不支持”和“性能下降”混在一起。

6. 结果矩阵应按操作类型分别阅读

混合负载结束后,IoT Benchmark 会分别列出写入和各类查询操作的成功数、失败数、吞吐量与延迟统计。读结果时,不能把所有行合并成一个总分。

观察到的情况 应怎样理解
写入吞吐下降,但查询延迟稳定 查询可能已开始与写入争用资源;先与纯写基线比较下降幅度。
写入稳定,但某类查询 P99 很高 说明在线写入未必受影响,但该查询形态可能不适合当前配置。
写入和查询都出现失败 先检查连接、配置、操作支持与超时,再讨论性能。
最近数据查询稳定,范围查询波动很大 不应把两类查询合并评价;它们访问的数据量和路径不同。
总吞吐提高,但写入行或查询行的失败数不为 0 这轮不能作为“性能更好”的结论依据。

特别是在读写混合时,写入吞吐量的下降并不天然是坏事。它可能反映了系统在为真实读请求分配资源。真正需要回答的是:在业务可接受的读延迟下,写入能力还剩多少;反过来,在写入不退化到不可接受的前提下,查询是否足够及时。

7. 三个常见误区

第一,把读写比例当成业务本身。4:1:0:0:1... 只是对业务行为的编码,不是一个通用最佳比例。比例改变前,应能说清楚对应多少用户、多少页面或多少告警任务。

第二,一轮测试同时混入所有查询类型。精确点、范围、聚合、倒序和集合查询的代价不同。刚开始就全部混入,只会让结果很热闹,却很难知道是哪个操作带来变化。

第三,忽略目标数据库的操作支持范围。IoT Benchmark 文档明确提示,不同数据库模块未必支持 OPERATION_PROPORTION 中的每一种操作。配置前应检查目标模块说明;若某种操作不支持,不能把失败数误解为系统性能不足。

8. 小结

模拟线上读写混合负载,真正要做的是把“谁在写、谁在读、读什么、读多频繁”翻译成一份可以验证的配置。开始时不妨只选择一种最典型的读请求,并以纯写测试作为对照。

在提交一份混合负载结果前,可以再检查一次:

  1. 写入负载是否仍与纯写基线保持一致?
  2. 每一种查询是否都有对应的线上行为?
  3. OPERATION_PROPORTION 中启用的操作是否被目标数据库模块支持?
  4. 是否分别记录了写入和各类查询的吞吐量、P99 与失败数?
  5. 当结果变化时,是否能够回到配置、日志和资源状态寻找证据?

完成这些检查后,读写混合测试就不再只是“给纯写测试加一点查询”,而能更接近线上系统真正同时面对的压力。下一篇可以继续讨论:当两个数据库版本或两种写入接口都能跑通同一份负载时,怎样做一场更公平的性能对比。

系列文章

  1. 第一篇:数据库基准测试工具是什么
  2. 第二篇:从一次时序数据库写入测试开始
  3. 第三篇:如何模拟线上写入负载
  4. 第四篇:如何读懂测试结果波动
  5. 第五篇:什么是读写混合负载
  6. 第六篇:如何模拟线上读写混合负载
  7. 第七篇:怎样做一场公平的性能对比
  8. 第八篇:怎样找到并发与批大小的合理区间
  9. 第九篇:如何模拟乱序写入
  10. 第十篇:不同写入接口该怎样比较
  11. 第十一篇:性能测试与正确性验证有什么区别
  12. 第十二篇:单机测试与集群测试有什么不同
  13. 第十三篇:怎样保存和复盘一轮测试
  14. 第十四篇:一份可复用的基准测试方案模板

基准测试工具之你不知道的两三事 | 6.如何模拟线上读写混合负载
https://spricoder.github.io/benchmarking/benchmarking-06/
作者
SpriCoder
发布于
2026年7月15日
许可协议