基准测试工具之你不知道的两三事 | 11. 性能测试与正确性验证有什么区别

基准测试工具之你不知道的两三事 | 11. 性能测试与正确性验证有什么区别

一轮基准测试结束后,我们通常最先看到的是吞吐量、平均延迟、P99 和失败数。如果吞吐足够高、失败数又是 0,能否直接说明测试通过?

不能。性能测试回答的是“系统处理得多快”,正确性验证回答的是“系统处理的结果是否符合预期”。一次请求返回成功,只能说明客户端收到了一次成功响应,并不能自动证明所有数据都以正确的设备、时间戳、类型和值落入数据库。

这一篇结合 IoT Benchmark 的实际实现,讲清性能与正确性应怎样分开测试,又怎样合并为一个可信结论。

在这里插入图片描述

1. 两类测试回答的是不同问题

维度 性能测试 正确性验证
核心问题 系统能处理多快、多稳? 写入和查询结果是否符合预期?
常见指标 吞吐量、平均延迟、P99、失败率、CPU、内存、磁盘与网络 记录数、时间戳、设备与测点、数据类型、具体值、查询结果集合
主要输入 工作负载配置与环境配置 可重复读取的预期数据集或可信参考结果
典型结论 “在当前并发和批大小下,系统达到某性能区间。” “预期写入的记录均可查询,且字段和值一致。”
失败表现 延迟抬升、吞吐平台、超时、资源饱和 少行、多行、错值、错类型、时间戳偏移、重复或覆盖

两者不是互相替代的关系。正确但很慢,说明系统能力不满足业务目标;很快但写错,性能数字则失去了意义。

2. 为什么“写入成功”仍可能写错

客户端成功调用写入接口,并不等于业务数据完全正确。实际测试中至少可能出现以下情况:

表面现象 可能隐藏的问题
请求返回成功 批内部分记录被忽略,客户端只看到整体响应。
失败数为 0 数据写入了错误设备、表、测点或时间分区。
写入点数符合预期 字段顺序、数据类型或具体值发生变化。
查询能返回数据 返回的是旧数据、重复数据,或被相同时间戳覆盖后的结果。
两个系统吞吐接近 两边真正持久化的数据量或写入语义并不相同。

因此,写入正确性至少应核对四层:

  1. 数量正确:预期多少行、多少点,实际是否一致。
  2. 定位正确:设备、表、测点、标签和时间戳是否正确。
  3. 内容正确:类型、空值语义和具体值是否一致。
  4. 查询正确:点查、范围查询和聚合是否返回预期结果。

如果测试还涉及重试、相同时间戳覆盖或乱序写入,则应再增加重复、幂等和最终可见性检查。

3. IoT Benchmark 的三段式验证流程

IoT Benchmark 提供了 generateDataMode、verificationWriteMode 和 verificationQueryMode。从代码看,它们组成了一条“准备预期数据 → 写入数据库 → 查询核对”的流程。

三个模式的职责并不相同:

模式 主要职责 它不能单独证明什么
generateDataMode 生成可复用的数据文件和 schema。 不会把数据写入待测数据库。
verificationWriteMode 从 FILE_PATH 读取批次、注册 schema 并执行写入,同时统计写入性能。 写入结束本身不代表数据已经逐值校验。
verificationQueryMode 再次读取同一数据集,构造验证查询,并将查询结果与预期时间戳和值比较。 不能替代高并发性能压测,也不是所有数据库适配器都必然支持。

这意味着,若只运行 verificationWriteMode 并看到失败数为 0,最多能说“写入调用没有被工具判定为失败”,不能说“所有数据已经验证正确”。

4. 验证查询在代码中怎样工作

在 verificationQueryMode 下,RealDataSetQueryClient 会从数据文件中取得一个 IBatch,再构造 VerificationQuery。这个对象同时携带:

  • 目标设备及其测点 schema;
  • 该批记录的时间戳;
  • 每个时间戳对应的预期值。

数据库适配器据此生成查询,读取这些时间戳对应的数据,并把返回值与预期值逐项比较。以当前 IoTDB 适配器为例,代码还会对 BLOB、DATE 等类型做格式转换,再进入比较。

这一逻辑比简单的 count(*) 更严格,因为“行数相同”仍可能包含错误值。但它也有明确边界:验证覆盖范围取决于对应数据库适配器的 verificationQuery 实现;接口默认实现会返回“不支持验证查询”。因此,开始测试前应先确认所用 DB_SWITCH 对应的适配器确实实现了该能力。

5. 一个必须检查的实现细节:错误日志不等于失败计数

按本文读取的本地版本 de2f8f3f,IoTDB 的验证查询在发现值不一致或返回行数不一致时会输出 ERROR 日志;但只要查询过程本身没有抛出异常,方法最后仍可能返回 Status(true, point)。

随后,DBWrapper 会把 Status.isOk() 为真的操作计入成功测量。也就是说:

最终报告中的查询失败数为 0,不足以证明逐值比较没有发现差异。

因此,正确性判定至少要同时查看:

证据 应检查的内容
验证日志 是否出现 Expected ... but was ...、预期行数不一致或查询错误。
返回点数 实际匹配点数是否等于根据数据集计算出的预期点数。
数据集规模 输入文件的设备数、记录数和测点数是否与测试方案一致。
抽样复核 手工或独立脚本查询若干设备与时间范围,确认值和类型。
服务端状态 是否仍存在异步写入、后台处理或尚未可见的数据。

这个细节并不意味着验证模式没有价值,而是提醒我们:日志和预期数量也是验证结果的一部分,不能只看最终汇总表。

6. 如何实际运行一轮验证

首先准备一份固定数据集。可以使用 generateDataMode 生成,也可以按 IoT Benchmark 要求准备 FILE_PATH 下的数据文件和 schema。

1
2
3
# 第一步:准备数据文件
BENCHMARK_WORK_MODE=generateDataMode
FILE_PATH=data/verification

随后清理目标范围并写入同一份数据:

1
2
3
4
5
# 第二步:写入验证数据,同时获得写入性能
BENCHMARK_WORK_MODE=verificationWriteMode
FILE_PATH=data/verification
CREATE_SCHEMA=true
IS_DELETE_DATA=true

写入完成后不要清理目标数据,再切换为查询验证:

1
2
3
# 第三步:查询并核对同一份数据
BENCHMARK_WORK_MODE=verificationQueryMode
FILE_PATH=data/verification

示例只表达模式切换关系。实际运行时还应完整冻结数据库版本、DB_SWITCH、数据模型、批大小、客户端数、数据文件校验值和测试日志。

7. 性能对比应该设置正确性门禁

当比较两个版本、接口或产品时,建议先定义门禁,再生成性能排名。

门禁 通过条件示例
写入调用门禁 无未解释的超时和失败,成功写入量达到预期。
数量门禁 设备、记录和数据点数量与固定数据集一致。
内容门禁 时间戳、类型和值的比较无差异。
查询门禁 关键点查、范围查询与聚合结果符合参考结果。
可见性门禁 在约定等待或刷新条件后,数据对新查询可见。

只有全部通过,吞吐量和延迟才进入最终结果表。若某个系统更快但未通过正确性门禁,应报告为“结果无效”或“当前配置下验证失败”,而不是继续参与排名。

8. 四个常见误区

第一,把失败数为 0 当作数据完全正确。失败数反映工具如何判定操作状态,不一定覆盖逐值差异。

第二,只比较总行数。相同时间戳覆盖、错设备和错字段都可能让总量看似正常。

第三,性能测试结束后直接清库。如果还没有保存配置、日志并执行查询验证,正确性证据会一并消失。

第四,一边验证,一边随意更换数据集或 schema。预期数据必须固定,否则比较对象本身已经发生变化。

9. 小结

性能测试关注吞吐、延迟和资源,正确性验证关注数量、位置、类型和值。IoT Benchmark 可以通过生成数据、验证写入和验证查询三个模式组织这条流程,但使用者仍需检查适配器支持范围、错误日志和预期点数。

最稳妥的原则是:先证明写对,再讨论写快;正确性门禁未通过的性能数字,不参与比较。 下一篇将继续讨论单机测试与集群测试的区别,以及节点、网络和数据分布如何改变测试结论。

系列文章

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

基准测试工具之你不知道的两三事 | 11. 性能测试与正确性验证有什么区别
https://spricoder.github.io/benchmarking/benchmarking-11/
作者
SpriCoder
发布于
2026年7月18日
许可协议