← 返回信息流
AI 资讯Hacker News·2 天前

数据库基准测试的谎言与真相

原标题:Lies, Damn Lies and Database Benchmarks

速览

文章深入剖析了数据库基准测试中常见的陷阱和误导性数据。指出许多基准测试未能真实反映生产环境下的性能表现。提醒技术决策者需审慎解读测试结果,避免被片面数据误导。

AI 深度解读

Lies, Damn Lies and Database Benchmarks:数据库基准测试的陷阱与真相

背景

在数据库领域,基准测试(Benchmarks)备受推崇。人们往往根据基准测试结果宣称某款数据库(Database X)是行业翘楚,因为它比另一款数据库(Database Y)快得多。然而,这种简单的线性思维往往掩盖了复杂的现实。

一个理想的基准测试本应像严格的奥林匹克田径比赛,精准践行“更快、更高、更强”的原则。但在现实中,当我们走近这些“运动员”时,会发现竞争规则变得荒谬:运动员必须在全力奔跑的同时准确吹奏《Yellow Submarine》(黄色潜水艇)这首歌。此时的冠军不再是单纯跑得最快的人,而是能在速度与无关技能之间取得最佳平衡的人。对于赛道上最快的短跑选手来说,他们完全可能因为吹错调而垫底。

这一类比同样适用于复杂的数据库基准测试,尤其是当比较不同类别的数据库时。一个完美、完全公平的数据库基准测试就像独角兽一样罕见。本文旨在通过操纵一个公开且广受认可的基准测试——ClickBench,来揭示所有数据库基准测试中普遍存在的偏差与陷阱。需要注意的是,本文并非针对 ClickBench 本身,而是将其作为探讨基准测试局限性的切入点。

核心内容

ClickBench 的测量机制

ClickBench 对每个系统运行相同的工作负载:一个包含约 1 亿行、105 列的单一 Web 分析表(著名的 hits 数据集),以及在其上的 43 个分析查询。每个引擎都附带一组 shell 脚本,流程固定:安装数据库、加载数据(从 CSV/TSV 导入,或指向可读取的外部 Parquet 文件),然后运行 43 个查询。

每个查询的测量分为两种模式:

  1. 冷运行(Cold run):清除操作系统页面缓存和数据库缓存后的首次执行。这捕捉了最坏情况,即没有任何缓存预热。
  2. 热运行(Hot run):根据 ClickBench 规则,“每个查询运行三次”,如果两次运行都成功,则取第 2 次和第 3 次运行中较短的时间。第一次运行旨在填充缓存,因此后续两次运行预期是最快的。

隐藏的非对称性

ClickBench 的规则要求重启服务器以实现真正的“冷运行”,但这在托管云服务(如 SnowflakeBigQueryRedshiftDatabricks)上无法实现。托管服务运行在提供商的硬件上,基准测试工具无法访问 shell、无法执行 drop_caches,也无法重启服务器。因此,托管服务的“冷运行”实际上从未被强制清空缓存,其结果总是偏向于托管系统,从而影响了综合评分。

为了公平起见,本文的所有测试均在同一台本地机器上自托管运行所有引擎,确保规则一致。但读者在对比托管与自托管系统的冷运行数据时需格外小心。

评分逻辑与内部计时陷阱

本文仅关注热运行结果,并比较整体得分而非单个查询。ClickBench 的得分计算方式为: $$ \text{ratio} = (0.01 + \text{hot_time}) / (0.01 + \text{baseline_time}) $$ 其中 baseline_time 是该查询中所有比较系统的最佳热运行时间。0.01 秒(10ms)的缓冲值防止了亚 10ms 的查询主导结果。最终得分是所有 43 个查询比率的几何平均值。得分越低越好,1.000 代表在所有查询中均为最快。失败的查询会受到严厉惩罚。

关键细微差别:每个 ClickBench 查询脚本记录的是引擎内部的查询时间(例如 DuckDBRun TimeClickHouse--timeDataFusionElapsed 等)。进程和客户端的启动时间并不包含在记录的数值中。因此,保持进程存活并不能通过消除启动时间来改变得分,因为启动时间从未被计入。它只能通过进程本地缓存的预热来影响得分。

测试环境

ClickBench 的参考运行使用 AWS 的 c6a.4xlarge VM。本文的测试在一台单机上进行:

  • CPU: AMD Ryzen 9 7900 (12 核 / 24 线程, 最高 5.49 GHz, 64 MiB L3)
  • 内存: 61 GiB RAM
  • 存储: NVMe SSD
  • 系统: Ubuntu 24.04

由于硬件不同,本文的绝对数值不能与 ClickBench 公共仪表板直接比较,仅用于同一机器上的引擎重排序。

测试对象与版本控制

基准测试的可复现性依赖于精确的版本号。本文使用的具体版本如下:

  • CrateDB: 使用 6.3.3(上游 apt install 拉取最新稳定版)。
  • QuestDB: 从 9.3.1 升级到 9.4.3。因为 9.3.1 存在两个 Bug:缺少 length_bytes() 函数导致查询 27 和 28 无法完成;配置项重命名导致超时设置失效。
  • PolarsHyper: 具体解析版本记录在支持仓库中。

场景 1:Parquet 瓦片竞争

首先进行简单测试:查询一个包含数据集的单一 Parquet 文件(约 14 GB)。选取了五个直接读取外部 Parquet 的引擎:DuckDBPolarsClickHouseDataFusionSalesforce Hyper

  • 默认行为:四个引擎(DuckDB, ClickHouse, DataFusion, Hyper)为每次查询启动全新的 CLI 或 Python 进程。
  • 例外Polars 作为长期运行的 Python 会话驱动。

在标准 ClickBench 结果中,DuckDBPolars 领先且几乎持平,DataFusionClickHouse 居中,Hyper 落后较多。

房间里的大象:进程生命周期

问题在于:每次查询都重启 CLI 或 Python 进程是否公平?这是 ClickBench issue #936 讨论的主题,标题为“Salesforce Hyper: Hot runs are measuring cold times”。对于某些引擎,标记为“热运行”的测试实际上是从头开始的冷启动。

在现实场景中,分析师通常会打开一个 DuckDB CLI 会话或交互式笔记本并保持其活跃状态,进程保持“温暖”。这也是传统客户端-服务器数据库的测量方式,因为服务器在查询之间不会停止。

为了模拟这一现实,本文进行了调整:保持每个引擎的进程在重复运行期间存活,通过长期会话(类似于工程师打开的 REPL)输入查询。除此之外,计时方法、数据和查询均保持不变。

关键要点

  • 基准测试的局限性:数据库基准测试往往像荒谬的互联网挑战赛,不仅比拼速度,还涉及与核心性能无关的技能平衡,导致最快者未必是冠军。
  • 托管与自托管的差异:ClickBench 的“冷运行”定义对托管云服务(如 Snowflake)不公平,因为无法强制清空缓存或重启服务。托管系统的冷运行数据天然偏向于缓存命中,导致评分偏差。
  • 内部计时器的盲区:ClickBench 记录的是引擎内部查询时间,不包含进程启动时间。因此,通过保持进程存活来优化得分,仅能通过缓存预热实现,而非消除启动开销。
  • 硬件一致性至关重要:不同硬件环境下的绝对数值不可比。只有在同一台机器上进行的相对重排序才具有可比性。
  • 版本控制的必要性:基准测试必须锁定精确的软件版本,以排除因版本更新引入的 Bug 或特性变化(如 QuestDB 的超时配置重命名问题)对结果的影响。
  • 进程生命周期的影响:标准的 ClickBench 测试中,多数引擎每次查询都重启进程,这实际上是在测量“冷启动”而非真正的“热运行”。保持进程存活更符合实际生产环境(如长期运行的分析会话)的使用模式。

意义与

查看原文 →questdb.com