PROD欧洲主权BaaS平台打开仪表板 →

性能 · 15 最小读取值

我们如何对后端进行基准测试:可重复的方法

Affane Daylami · Fondateur · 2026年7月8日

返回博客

没有方法的基准数据证明不了什么。 “p95 under X ms”、“冷启动少于 Y ms”——任何人都可以在营销页面上写下这些内容。证明某事的是方法:使用的设备、测试的持续时间、测量协议以及第三方以相同方式复制它的可能性。

该英文文本是根据法文原文自动生成的,尚未经过审查。
该页面已自动翻译。英文版具有权威性。

本文通过在发布任何性能数据之前记录我们将在 Aurabase 应用的协议,回答了一个具体问题——如何以可重复的方式对后端 API 进行基准测试。不是结果:是方法。任何已在本网站其他地方发布的测量结果(特别是在我们的 性能页面上)如果尚未依赖此协议,则应被视为未经验证,直至另行通知。

要点

迄今为止,还没有遵循已发布的、可重现的协议的 Aurabase 性能结果 - 本文记录了我们将用于生成这些结果的方法,而不是已经获得的结果。该存储库已包含一个 3 级 测试套件:3 个 crate 上的 Criterion.rs 微基准测试、8 个 HTTP/WebSocket 场景的 k6 负载测试,以及用于直接 Postgres 与 API 进行百分位计算比较的 Python 脚本。完整的协议——测量持续时间、百分位数而不是平均值、环境隔离、版本和日期披露——基于经过验证的外部源:PostgreSQL、Criterion.rs、k6 (Grafana)、HdrHistogram、PlanetScale 和 Convex。任何已在本网站其他地方发布但未追溯到本协议的性能声明均应被视为未经验证。

#
编辑立场

为什么我们不公布裸数据

Convex 是一个竞争性响应式后端的发行商,它已公开与其技术团队所称的数据库提供商之间的“条形图战争”保持距离。他的公式很直接:“这是缩放剧院,而不是缩放”——缩放剧院,而不是真正的缩放(stack.convex.dev/on-competitive-benchmarks,Stack 技术博客,2026 年 8 月 23 日访问)。

它的核心论点是:比较具有不同一致性、拓扑或定价模型保证的两个系统的基准通常不会测试相同的东西,即使它声称这样做 - “基准实际上并没有测试相同的东西”。这种反应在业内有一个名字:基准测试,发布的数字是根据其营销效果而不是其方法严谨性而选择的。

我们的回应并不是拒绝衡量——无限期地拒绝公布某个数字就像公布未经证实的数据一样不诚实。在声称测量了任何东西之前,首先要记录我们将如何测量、使用什么工具以及在什么条件下进行测量。这也是有用的比较(例如我们的 Aurabase 与 Appwrite 比较,其中记录了可验证的架构差异)与没有通用协议的性能数据比较的区别。

这对于必须在技术委员会中捍卫后端选择的技术主管或 CTO 来说尤其重要:无法追溯到方法的数字无法在第一个有些棘手的问题中幸存下来。记录下来的协议是自我保护的——您可以展示脚本、经过测试的版本,并在必要时在某人面前再次运行测试。

#
诊断

是什么让大多数后端基准具有误导性

两个陷阱系统地出现:比较不同的拓扑而不报告它,以及以精确隐藏对用户最重要的暂停的方式测量延迟。

关于第一点,PlanetScale 明确记录了其硬件奇偶校验约束:每个比较环境必须在同一云区域中等于或大于参考实例的计算资源(vCPU、RAM)上运行(Planetscale.com/benchmarks,“Telescope”方法,2026 年 8 月 23 日访问)。如果没有这种纪律,延迟差距可能只是反映了更大的机器,而不是更快的架构。

同样的原理也适用于缓存状态和网络拓扑。刚启动的实例(冷 Postgres 缓存、空连接池、尚未缓存的查询计划)的响应结构比在稳定负载下运行一小时的实例慢。来自与数据库相同区域的查询在结构上比跨区域查询响应更快。没有指定任何一项的两个基准根本不具有可比性,即使它们显示相同的单位。

关于第二点,陷阱称为 协调遗漏。 HdrHistogram 是 Gil Tene 创建的延迟测量参考项目,它是这样解释的:当负载生成器在发送下一个请求之前等待请求的响应(闭环)时,服务暂停会自动减少暂停期间发送的请求数量,从而减少记录的高延迟测量数量(github.com/HdrHistogram/HdrHistogram,于 8 月 23 日访问, 2026)。该项目给出了一个具体且量化的示例:在一个假设系统中,每 10 毫秒对其延迟进行一次采样,持续 200 秒,测试中间 100 秒的单次暂停就足以生成一个直方图(无需校正),其中大约 99.99% 的响应似乎都在 1 毫秒以下 - 尽管在这一单次暂停中已经过去了一半的实际时间。

经典陷阱

仅在收到先前响应后才发送请求的闭环负载测试系统性地低估了长时间的暂停。它显示的 p99 可能比真实用户体验到的现实更好 - 不是因为系统速度快,而是因为测量协议“忘记”在暂停时发送查询。

#
统计数据

为什么平均值是:p50、p95、p99

当二十分之一的请求花费五倍的时间时,平均延迟可能看起来非常好。这正是百分位数所揭示的内容以及平均值在结构上所隐藏的内容。

从机制上讲,百分位没有什么神秘之处:按升序对所有测量到的延迟进行排序,然后在相应位置取值。在 1000 个排序查询中,p50 是第 500 个值,p95 是第 950 个值,p99 是第 990 个值。 1000 个请求中的一个异常缓慢的请求就足以让 p99 移动 - 正是它对罕见情况的敏感性使其有用,而同样的孤立请求对平均值几乎没有影响。

告密标志:pgbench(官方 PostgreSQL 基准测试工具)默认显示的文本报告给出平均值和 标准差,而不是百分位数(postgresql.org/docs/current/pgbench.html,访问日期:2026 年 8 月 23 日)。其官方文档还警告:“永远不要相信任何只运行几秒钟的测试”——永远不要相信只运行几秒钟的测试,这适用于持续时间和所选指标。

k6 是我们套件第 2 级使用的加载工具,通过以百分位数表示的阈值解决了这个问题:p(95)<500 语法定义了通过/失败标准 - 95% 的请求必须在 500 毫秒内响应 - 直接在测试配置中(grafana.com/docs/k6,于 2026 年 8 月 23 日咨询)。

p50(中位数)一半的查询比该值快完全隐藏分布尾部
p95二十分之一的查询速度较慢第一个不满意的用户出现的区域
p99百分之一的查询速度较慢如果协议设计不当,对协调遗漏最敏感
#
已签入代码

我们的存储库中已存在 3 个基准级别

在没有真正工具的情况下发布方法论只是另一种形式的戏剧。 Aurabase 存储库中的 benchmarks/ 文件夹已包含一个 3 级套件,其结构受到公共 Supabase 方法的启发 - 工具已存在,但测量和注明日期的结果尚不存在。

3
测试级别
微、HTTP 负载、比较
3
基准板条箱
aura-crypto、aura-db-适配器、aura-core
8
K6 脚本
7 个连接到 Makefile,1 个等待

级别 1 — 微基准 Criterion.rs

Cargo 工作区 的三个箱子具有专用的 CPU 绑定基准: aura-crypto (Argon2 哈希、JWT HS256 — PostgREST 的生成、验证和签名、AES-GCM 加密)、 aura-db-adapters (解析过滤器和 PostgREST 格式的 select — eq.、 gte.、 in.()、关系嵌入)和 aura-core (JSON 序列化、schema_name解析、UUID 验证)。

libs/aura-crypto/benches/crypto_bench.rsrust
// 从存储库中实际提取
let mut group = c.benchmark_group("jwt/hs256");

group.bench_function("generate", |b| {
    b.iter(|| jwt::generate_access_token(
        black_box(user_id), black_box(project_id),
        black_box("authenticated"), black_box(SECRET),
    ))
});

group.bench_function("validate", |b| {
    b.iter(|| jwt::validate_token(black_box(&token), black_box(SECRET)))
});

aura-db-adapters 专门测量解析 PostgREST 格式查询的成本 - 过滤器的四种情况(simple_4、 complex_10、 or_group、 in_large_50 具有 50 个值)和 select 的四种情况(单列、 *、一个关系嵌入、五个嵌入)。这是全局负载测试中不可见的成本:对复杂 or.(...) 过滤器分析的回归几乎不会改变很少使用的端点的 p95 中的任何内容,但会在高流量的端点上进行测量 - 因此有兴趣将其隔离在微基准测试中,而不是仅仅依赖于级别 2。

aura-core 采用不同的方法:它不是测量原始时间,而是测量网关和服务之间交换的内部 NatsRequest/NatsResponse 消息的 JSON 序列化和反序列化的 吞吐量 (Throughput::Bytes) — 具有三种实际负载大小(最小请求、嵌套 JSON 正文的请求、50 行列表响应)。

libs/aura-core/benches/core_bench.rsrust
group.throughput(Throughput::Bytes(
    serde_json::to_vec(&small).unwrap().len() as u64
));
group.bench_function("NatsRequest/small", |b| {
    b.iter(|| serde_json::to_vec(black_box(&small)).unwrap())
});

Criterion.rs 不仅仅对循环进行计时。它首先运行一个预热阶段来填充 CPU/OS 缓存,使用 Tukey 方法的修改版本检测异常值(不将其从数据集中排除),通过引导大量重新采样的样本来计算置信区间,并通过 Student 统计测试检测两次运行之间的性能回归,并使用可配置的噪声阈值(通常为 ±1%)来忽略统计上不显着的变化 () bheisler.github.io/criterion.rs/book/analysis.html,访问日期:2026 年 8 月 23 日)。

本地可重现

每次 Criterion 运行都会在 target/criterion/ 中生成详细的 HTML 报告 — 分布、回归图、与之前运行的比较。正是这种关系,而不仅仅是一条终端线,必须有一种严肃的方法论才能使其再生成为可能。

2 级 — k6 负载测试

八个 k6 脚本覆盖数据平面侧的 网关: health (延迟基线), auth-flow (注册 → 登录 → 刷新 → 注销), crud-read 和 crud-write, storage (上传/下载), realtime-ws, breakpoint (负载增加直到失败)和 supabase-compare。七个连接到专用的 Makefile 目标 - supabase-compare.js 存在于存储库中,但还没有目标,本文按原样记录而不是掩盖它的情况。

benchmarks/k6/scenarios/crud-read.jsjavascript
export const options = {
  scenarios: {
    crud_read: {
      executor: 'ramping-vus',
      startVUs: 10,
      stages: [
        { duration: '30s', target: 50 },
        { duration: '1m', target: 200 },
        { duration: '30s', target: 0 },
      ],
    },
  },
  thresholds: THRESHOLDS_READ,
}

共享配置定义每个操作类型的阈值。这些是测试每次运行时检查的通过/失败标准 - 不是已经测量的结果:

阅读(获取)p95 < 500 毫秒 · p99 < 1000 毫秒配置 k6 (benchmarks/k6/lib/config.js)
写作(帖子/补丁)p95 < 300 毫秒 · p99 < 1000 毫秒配置k6
验证(登录/刷新)p95 < 300 毫秒 · p99 < 1000 毫秒配置k6
存储(上传/下载)p95 < 500 毫秒 · p99 < 2000 毫秒配置k6
错误率,所有场景< 1 %配置k6
写这篇文章时发现不一致的地方

benchmarks/ 文件夹中的 README.md 记录了 < 200ms (“Supabase SLO”)处 p95 的读取阈值,而 benchmarks/k6/lib/config.js(测试运行的阈值)中实际应用的阈值是 p(95)<500。这两个文件是相互派生的。这是一个具体的例子,是在阅读本文的源代码时发现的,说明了为什么协议应该有单一版本的事实来源,而不是记录在两个地方:没有它,即使是一个试图严谨的团队最终也会发布相互冲突的阈值。

第 3 级 — 直接 PostgreSQL 与 API 比较

Python 脚本 (direct_vs_api.py) 通过将直接 psycopg2 请求与同一操作上的 HTTP 调用进行比较来测量网关 + 服务层的实际开销 - 列表、按 id 一次性读取、过滤和排序读取。每次测量都会在定时循环之前进行 10 次迭代的预热,然后计算平均值、p50、p95、p99 和每秒操作的吞吐量。

benchmarks/comparison/direct_vs_api.pypython
def percentile(data, p):
    k = (len(data) - 1) * (p / 100)
    f = int(k)
    c = f + 1
    if c >= len(data):
        return data[f]
    return data[f] + (k - f) * (data[c] - data[f])

第二个脚本 (aurabase_vs_supabase.py) 将相同的预热和百分位计算逻辑应用于与本地 Supabase 实例(Supabase CLI,默认情况下 localhost:54321 )的头对头比较 — 同一台机器,两者的本地网络相同,这正是 PlanetScale 为自己的比较而记录的环境奇偶校验规则。

编排脚本(collect_baseline.sh, Makefile的目标 bench-baseline )连接三个级别 - 3 个 crate 上的 Criterion、k6 场景的子集(今天的health 和 crud-read ,还不是全部 8 个),然后是 Python 比较 - 并将日志、JSON 和 Criterion HTML 报告写入具有唯一时间戳的文件夹: benchmarks/results/AAAAMMJJ_HHMMSS/。这正是在一次可重复运行中过时披露的反映,以下部分在完整的协议中形式化。

#
方法论

我们在发布图形之前将应用的协议

八项承诺,每一项都以公认的第三方工具或项目已经记录的实践为基础,而不是临时发明的。

  1. 预热与测量分开。 Criterion.rs 在计时之前填充 CPU/OS 缓存; pgbench 明确建议永远不要相信只持续几秒钟的跑步。
  2. 固定持续时间,而不是固定的迭代次数。 负载需要时间收敛 - 这是 stages k6 和 pgbench的 -T 标志的作用。
  3. 百分位数,而不仅仅是平均值 — 如果负载生成器在闭环中运行,则要对协调遗漏保持积极警惕。
  4. 详细记录的环境:测试的服务的 git 提交、PostgreSQL 版本、硬件规格、加载工具版本。正是出于这个原因,PlanetScale 记录了其确切的 TPCC 参数(TABLES=20、 SCALE=250、~500 GB) - 没有这些详细信息,没有人可以重现运行。
  5. 带时间戳和版本化的结果,营销页面上不会刻有没有日期的单个数字。当前的工具已写入过时的文件中;有必要将此反射扩展到任何公开发布的测量结果,并像任何其他环境变量一样记录托管区域(请参阅我们关于 欧盟托管主权的指南,只要数字取决于给定区域,就相关)。
  6. 脚本和原始数据与汇总结果一起发布,而不仅仅是最终平均值。 PlanetScale 甚至邀请读者在专门的地址上报告方法上的错误——我们认为这种做法是健康的,并且我们希望恢复这种做法。
  7. 公布的吞吐量和延迟,而不仅仅是其中之一。 系统可以在低负载下具有出色的延迟,并且随着并发性的增加,吞吐量会崩溃 - 这正是我们的 k6 套件的 breakpoint 场景(扩展至崩溃)旨在揭示的内容,以及 Criterion 的 Throughput::Bytes 微基准测试在功能级别捕获的内容。
  8. 在宣布改进之前存在显着差距。 两次运行之间百分之几的变化可能是测量噪声而不是真正的增益 - Criterion.rs 计算观察到的差异是由于偶然因素造成的概率,然后将其视为回归或改进。一个孤立的数字,如果没有经过验证,就只是一个统计轶事。
#
编辑承诺

我们不会做什么

这份清单与上面的积极协议一样重要。

  • 比较不同的拓扑(自托管与托管、冷实例与预热实例),但未明确报告。
  • 保留十个中最好的一个,而不提及其他九个。
  • 发布没有日期、没有服务版本、没有复制脚本的图。
  • 重新发布现有的营销数据,只要它不能追溯到此协议。
  • 如果竞争对手没有以同等方式发布自己的方法论,则根据原始绩效数据与竞争对手进行比较 - 数字与沉默不是比较,而是口号。
一个具体的例子,已经内部更正

像“冷启动小于 1 毫秒”这样的数字在没有可重复基准支持的情况下被流传。现在,它在内部被视为 不受支持的,并且不应被视为产品的测量特性,直到任何带有已发布方法的过时测量确认为止。这正是该协议存在的目的是为了防止重复的声明。

#
可重复

用于对任何后端进行基准测试的最小协议

该协议不依赖于任何特定的 Aurabase 工具 - 您现在可以将其应用到您自己的 API 中。

  1. 在工具之前设置负载:只读、写入、为您的应用程序提供真实的组合——而不是从另一个项目复制的通用比率。
  2. 将预热阶段与测量阶段明确分开。
  3. 运行测试足够长的时间——几分钟,而不是几秒。
  4. 以百分位数 (p50/p95/p99) 衡量,而不是单独衡量平均值。
  5. 验证您的负载生成器是否处于闭环状态,或者更正分析中的协调遗漏。
  6. 隔离测试环境——没有吵闹的邻居,没有竞争的后台任务。
  7. 发布测试版本、日期、硬件规格和脚本——而不仅仅是最终结果。

在纯 Postgres 基础上,该协议需要一个命令 pgbench — 20 个并发客户端分布在 4 个线程上,持续 5 分钟,每 10 秒生成一次进度报告:

terminalbash
# 初始化测试数据集(比例因子 >= 客户端数量)
pgbench -i -s 50 ma_base

# -c 并发客户端、-j 线程、-T 持续时间(以秒为单位)、-P 报告间隔
pgbench -c 20 -j 4 -T 300 -P 10 ma_base
#
工具

参考工具,按级别

五种工具,每种都适合不同级别的堆栈,没有一个可以替代其他工具。

麦克风(功能)标准.rs纯CPU,引导统计
SQL查询基准测试类 TPC-B 事务、tps 和延迟
HTTP/WS 负载k6(格拉法纳)百分位数、通过/失败阈值
大规模 OLTPsysbench + TPCC(望远镜方法)QPS,性能成本
测量校正高动态范围直方图补偿协调遗漏
#
常见问题解答

常见问题解答

为什么 Aurabase 还没有发布基准数据?+
因为迄今为止还没有根据已发布且可重现的协议测量性能数据。例如,像“冷启动小于 1 毫秒”这样的数字在没有可重复基准支持的情况下就被传播了:今天它被视为未经证实的,不应被视为产品的测量特性。本文记录了我们在发布结果之前将遵循的协议,正是为了避免重复此类声明。
什么是 p95 或 p99 百分位,为什么不是平均值?+
p95 是低于该值的响应时间,95% 的请求都会被发现 - 因此,每 20 个请求中有 1 个会变慢。 p99 将此阈值推至 100 个请求中有 1 个。平均值隐藏了这些慢速请求,因为它在大量快速请求中稀释了它们;百分位数隔离了用户实际注意到的分布的尾部。
什么是“协调遗漏”?+
这是 HdrHistogram 项目描述的一种测量偏差:当加载工具在发送下一个请求之前等待对请求的响应(闭环)时,服务暂停会机械地减少在此暂停期间记录的慢速请求的数量。最终结果可以显示比真实用户体验更好的延迟。
我们可以自己重现这些测试吗?+
这里描述的协议——百分位数、单独预热、记录环境、注明日期的结果——适用于任何 API,具有公共工具(k6、pgbench、Criterion.rs、sysbench)。内部 Aurabase 工具(基准/存储库文件夹)当前用于开发,尚未打包为一键式公共套件。创建一个项目,使用您自己的 k6 脚本测试 Aurabase API 上您自己的负载。
测量的百分位数和 SLA 阈值之间有什么区别?+
百分位(p95、p99)是根据实际测量结果计算的统计数据。 SLA 阈值(或 k6 阈值,如 p(95)<500)是预先设置的目标,测试以通过/失败模式验证该目标。混淆两者会导致将未实现的目标呈现为已获得的结果——这正是该协议要求在每个发布的数据中保持明确的区别。
除了延迟之外,为什么还要测量吞吐量?+
系统可以在低负载下快速响应,并且一旦超过并发阈值,其延迟就会突然降低——仅延迟并不能显示该阈值在哪里。测量吞吐量(请求或每秒字节数)和延迟揭示了这个临界点,这是我们的 k6 套件的断点场景专门设计的目的。

准备好部署了吗?

五分钟内完成您的后端。

无需信用卡 · 500 MB 免费 · 50,000 MAU