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

性能 · 11 最小读取值

PostgREST:生产中的基准和实际限制

Affane Daylami · Fondateur · 2026年5月18日

返回博客

PostgREST 本身几乎从来都不是瓶颈。在专用 Aurabase 实例上,副本使用 50 到 250 毫核 CPU 和 64 到 128 MB RAM 运行。它是一个轻量级的 Haskell 二进制文件,可以将 HTTP 请求转换为 SQL,仅此而已。生产中出现的真正限制在其他地方。其中四个最常出现:其副本消耗的 Postgres 连接的预算,以及 MVCC 下精确 COUNT 的成本。响应截断也可以在标头中保持不可见,每次模式迁移后的延迟窗口也是如此。

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

我们关于 PostgREST 兼容性 的文章详细介绍了服务器在功能上涵盖的内容(过滤器、嵌入、RPC、RLS)以及它留给您的内容。这个来自其他地方。它以 Aurabase 代码和官方 PostgREST 文档作为来源,记录了 PostgREST 实际上在何处以及为何大规模停滞。我们不会在这里复制我们自己没有运行过的负载组。我们的 基准测试方法 解释了为什么没有发布协议的孤立数字对我们来说似乎不可靠。

要点

  • PostgREST 本身是轻量级的:专用 Aurabase 实例上的每个副本有 50 到 250 毫核 CPU、64 到 128 MB RAM。原始 HTTP 吞吐量几乎从来都不是生产中的限制因素。
  • 真正的上限是 Postgres 连接预算:PGRST_DB_POOL × 副本数。在 Aurabase 代码中验证:每个项目在专用级别 (10×2) 上有 20 个连接,在共享级别 (2×2) 上有 4 个连接。这是为了在同一个 max_connections上容纳更多租户而特意选择的。
  • Prefer: count=exact 强制在大型表上进行昂贵的 MVCC 扫描。 PostgREST 记录了两种更便宜的替代方案: count=planned 和 count=estimated,总共花费大约。
  • db-max-rows 上限(Aurabase 默认为 1000 行)会截断响应,而不在 Content-Range 中报告它(在实际条件下测量,详细信息如下)。
  • DDL 迁移后,PostgREST 架构缓存会异步重新加载。 Aurabase 网关在放弃之前重试最多 8 次(最坏情况下累计约 3.5 秒),该行为直接记录在代码中。
#
方法论

PostgREST 基准衡量什么,不衡量什么

PostgREST 上的 HTTP 吞吐量测试主要测量 Postgres,很少测量 PostgREST。服务器是基础前面的一个薄薄的翻译层。在绝大多数现实世界的负载中,响应时间主要由执行的 SQL 查询决定,而不是生成它的进程。

PostgREST 项目在 GitHub 上维护了该主题的专用存储库 PostgREST/postgrest-benchmark,它跟踪版本之间的吞吐量变化,而不是发布孤立的营销数据。我们既没有在这里表演也没有重新发布它。其结果取决于硬件、原理图尺寸和测试场景,这正是我们自己的 基准协议 在引用数字之前需要记录的变量。

在 PostgREST 下面,pgbench 测量真正重要的层:并发负载下的 SQL 事务时间。这是官方 PostgreSQL 基准测试工具(postgresql.org/docs/current/pgbench.html,于 2026 年 8 月 24 日访问)。本文不是在这里重现该协议,而是记录了生产中 PostgREST 的四个具体架构限制,每个限制都在 Aurabase 源代码或官方项目文档中进行了验证。

#
已签入代码

Aurabase 上 PostgREST 实例的实际占用空间

每个 Aurabase Postgres 引擎项目都会收到两个与其集群位于同一位置的专用 PostgREST 副本。部署它们的 Kubernetes 清单设置了适度的资源。

50-250m
每个副本的 CPU
请求→限制
64-128
每个副本 MB RAM
请求→限制
2
按项目划分的副本
高可用性(P22)

这些副本真正消耗的不是 CPU:它们是与 Postgres 主数据库的连接。每个 PostgREST 实例都直接连接到主实例 (-rw),无需通过为租户部署的 PgBouncer 池化器。我们在关于 PostgREST 兼容性的文章中已经详细介绍了此选择:LISTEN/NOTIFY 模式重新加载机制需要持久连接,与事务模式下的池化器不兼容。本文添加的内容是:就连接而言,实际成本是多少,以及峰值在哪里。

每个副本的池大小 (PGRST_DB_POOL) 故意根据项目级别而有所不同,并在 k8s_tenant.rs中进行验证,该函数为每个项目构建 PostgREST 清单:

轴承PGRST_DB_POOL / 副本复制品连接/觉醒项目
专用(高级,A1)10(PostgREST 默认值)220
共享(舰队、免费/专业/团队)2(Aurabase 默认值,降低)24
deploy/cnpg/tenant-postgrest.yaml (真实提取物,由供应者替代的价值)yaml
# 主节点上每个副本的连接指纹。
env:
  - { name: PGRST_DB_POOL, value: "PGRST_DB_POOL_VALUE" }  # 10 个(专用)或 2 个(共享)

在专用级别上,约束被放松:一个项目有自己的 CNPG 集群,因此有自己的 max_connections,没有多余的邻居。在共享级别上,来自同一组织的多个项目共享一个集群:正是这种背景使得连接预算具有决定性,这将在下一节中进行阐述。

#
真正的天花板

连接预算决定同时运行的租户数量

在共享 Postgres 集群上,限制同时活动项目数量的不是 HTTP 吞吐量。这是与可用的 max_connections相比,它们的 PostgREST 实例在主数据库上保持打开的连接数。

Aurabase 直接从实际集群限制中得出此预算,在 fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé中检查,下限为 1。固定保留为 10 个连接(超级用户、CNPG 实例管理器、指标导出器、配置者管理边际)。对于交付的缺陷(每个副本 2 个、2 个副本或每个唤醒项目 4 个连接的池),计算根据集群的大小级别给出三种不同的预算。

按级别、共享 Postgres 集群同时活动项目的预算可用级别:5 个同时活动项目(max_connections 50,pooler 20)。专业级别:7(最大连接数 100,池化器 60)。团队级别:10(max_connections 200,po​​oler 150)。公式源自 Aurabase 代码 (fleet.rs::derive_wake_budget),固定保留 10 个连接,每个唤醒项目 4 个连接。024681012免费(最大连接数 50)5个项目专业版(最大连接数 100)7个项目团队(最大连接数 200)10个项目

来源:源自 fleet.rs::derive_wake_budget 和 wake_budget_for_org_plan,Aurabase 代码,于 2026 年 8 月 24 日重读。

此预算不是所拥有项目的配额:一个 team 组织可以容纳 50 个项目,其中大部分处于休眠状态。这是并发上限:主节点上可以同时保持打开连接的项目数量。超出预算的唤醒不会失败,它会被推迟,直到同级项目重新进入睡眠状态并签入同一文件。调整 max_connections 本身大小的主题在我们关于 max_connections调整的文章中进行了扩展,以及 专用与共享基础中的专用/互用权衡作为一个整体。

#
隐性成本

为什么首选:count=exact 会减慢大表上的查询速度

要求精确的总数会迫使 Postgres 对每个查询的过滤结果的可见行进行计数,这是一个随表增长的成本,而不是免费操作。

PostgreSQL 不维护任何开箱即用的索引行计数器。在 MVCC 下,行的可见性取决于读取它的事务。因此,精确的 COUNT(*) 必须访问候选行而不是读取预先计算的值。这是 Postgres 生态系统中一个有据可查的结构限制,包括像 ClickHouse 这样的分析供应商,它们将自己的近似计数器与 Postgres 事务行为进行比较。

terminalbash
# 在大表上代价高昂:强制对过滤结果进行 MVCC 扫描
curl "https://<gateway>/v1/db/<project_id>/orders?status=eq.paid" \
  -H "apikey: <clé>" -H "Prefer: count=exact"

# PostgREST 记录的更便宜的替代方案
  -H "Prefer: count=planned"   # 通过规划器进行估计
  -H "Prefer: count=estimated" # 计划超出阈值,恰好低于

PostgREST 原生记录了这三种计数策略(postgrest.org,2026 年 8 月 24 日访问)。 exact 策略保证以扫描价格计算总计。 planned 从查询规划器返回几乎免费的估计。 estimated 根据阈值自动在两者之间切换。这个选择不是装饰性的:需要在包含数百万行的表上使用 count=exact 的分页需要为每页上的扫描付费,即使用户从不查阅最后一页。

#
实际测量

如果没有 count=exact,db-max-rows 的截断是不可见的

行上限可以截断 PostgREST 响应,而无需在正文或标头中进行任何指示,除非明确请求确切的总数。我们在专用 Aurabase 实例上的真实条件下对其进行了测量,而不是假设。

在带有 PGRST_DB_MAX_ROWS=5的 10 行测试表上,PostgREST v12.2.3 在两种截然不同的情况下呈现完全相同的 Content-Range 标头:

查询渲染线内容范围元(Aurabase)
?limit=50(无计数)5/10 真实0-4/*{}
限制=50&计数=精确5/10 真实0-4/10{总计:10}

如果没有 count=exact,5 行的响应与实际上只包含 5 行的表无法区分: Content-Range: 0-4/* 描述了呈现的行,而不是应用的限制。在这种情况下,实际的上限不会出现在任何地方,直接在 Aurabase SDK 的 PostgREST 路径上测量。

PostgREST 上任何分页的后果

如果您的 PostgREST 部署设置 db-max-rows(Aurabase 默认为 1000),则将 data.length 与请求的限制进行比较以检测整页的客户端可能会出错。一旦服务器上限低于此限制,就会出现该错误。唯一可靠的信号是将接收到的行数与 count=exact返回的 total 进行比较,这直接发挥了上一节中描述的成本权衡。

#
延迟延迟

迁移后重新加载架构缓存

PostgREST 在启动时将 Postgres 架构保留在内存中。在执行 DDL(创建表、添加列)之后,必须在新路由响应之前重新加载此缓存,并且此重新加载是异步的。

到达此窗口的写入可能会收到瞬态 404(缓存尚未更新),即使该表确实存在于 Postgres 端。 Aurabase 网关通过有界重试循环吸收此问题,在 postgrest_proxy.rs中进行了验证:最多 8 次尝试,增加回退(250 毫秒加上每次尝试 100 毫秒),在最坏的情况下累计 3.5 秒。此机制仅影响写入,从不影响读取。

代码本身记录的细节

网关不会发出任何重新加载信号:它只是等待。唯一真正的触发器是 DDL 路径上的数据库服务发出的 pg_notify('pgrst', 'reload schema')。如果迁移路径忘记发出此信号,则 8 次尝试会耗尽永远不会更改的缓存,这是代码注释中记录的风险,而不是伪装的。

对于自托管 PostgREST 部署,本课程进行了概括。应用程序中的每个 DDL 路径都应通过 NOTIFY 或向进程发送 SIGUSR1 信号来触发重新加载。否则,迁移会在部署后立即产生伪装成间歇性错误的 p99 延迟峰值。

#
总结

架构的切片,而不是原始吞吐量

这里记录的四个限制有一个共同点:在独立的 HTTP 吞吐量测试中看不到任何限制,但所有四个限制都决定了 PostgREST 部署是否可以扩展到生产环境。

  • 连接预算:限制共享集群上同时活动的租户数量,无论每个租户的吞吐量如何。
  • 精确 COUNT 的成本:随表增长,而不是随负载增长;被 planned/estimated绕过。
  • 无提示截断:正确配置的行上限仍然可以破坏检测不良的分页。
  • 架构重新加载:每次迁移后的延迟窗口,如果重新加载信号连接良好则有界,否则无限制。

无论您是在自托管 PostgREST、Hasura 风格的 GraphQL 层还是自定义 API 之间进行选择,这四个轴都是比孤立的 req/s 数字更好的比较点。请参阅我们的比较 PostgREST 与 Hasura 与自定义 API。数据库前面的池化器的选择同样重要:我们的比较 PgBouncer vs Supavisor vs PgCat 详细说明了为什么 PostgREST 无法在事务模式下通过池化器。

#
常见问题解答

常见问题解答

PostgREST 足够快以进行大规模生产吗?+
PostgREST 本身是一个轻量级的进程。在专用 Aurabase 实例上,副本运行 50 到 250 毫核 CPU 和 64 到 128 MB RAM,这在项目的 Kubernetes 清单中进行了验证。原始 HTTP 吞吐量几乎从来都不是生产中的限制因素。决定整个事情是否可扩展的是 Postgres 连接预算、精确 COUNT 的成本以及架构缓存,而不是单独的 PostgREST 二进制文件的速度。
我如何知道我的 PostgREST 响应是否被 db-max-rows 截断?+
PostgREST 返回的 Content-Range 标头从未说过这一点。每个 db-max-rows 上限为 5 行的响应与实际上仅包含 5 行的表没有区别(在专用 Aurabase 实例上的实际条件下测量)。检测它的唯一可靠方法是将收到的行数与 Prefer: count=exact 返回的总行数进行比较,如果没有此标头,截断将保持不可见。
确切的 COUNT 是否总是会减慢 PostgREST 请求的速度?+
首选:count=exact 强制 Postgres 对每个查询的过滤结果的可见行进行计数,由于 MVCC,成本会随着表大小的增加而增加。 Postgres 不维护开箱即用的索引行计数器。 PostgREST 提供了两种更便宜的替代方案,count=planned(通过调度程序进行估计)和 count=estimated(超出阈值自动切换),这在其官方文档中进行了记录。
PostgREST 消耗多少个 Postgres 连接?+
它完全取决于 PGRST_DB_POOL 乘以副本数量。在 Aurabase 代码中验证:专用实例(高级层)默认为每个副本打开 10 个连接,或者超过 2 个副本总共打开 20 个连接。共享级别自愿将此池降低到每个副本 2 个连接,或每个唤醒项目 4 个连接,以便在共享集群的相同 max_connections 预算上容纳更多租户。
有官方的 PostgREST 基准吗?+
该项目在 GitHub 上维护一个专用存储库 PostgREST/postgrest-benchmark,它跟踪各个版本的吞吐量变化,而不是发布孤立的营销数据。我们既没有在这里表演也没有重新发布它。本文记录了我们的代码和官方 PostgREST 文档中经过验证的架构限制,而不是我们自己复制的工作台。
#
结论

要记住什么

PostgREST 在单独的 HTTP 负载下几乎永远不会崩溃:它的架构对此来说太简单了。生产中的中断在于它周围的东西:它的副本保持开放的连接数量,确切的总成本是多少。这还包括截断是否仍然可见,以及迁移后窗口持续多长时间。

这四个限制并非 Aurabase 特有的:它们适用于任何 PostgREST 部署(自托管或托管)。此代码显示的是多租户部署如何使它们变得明确,而不是让它们在生产中令人惊讶。

准备好部署了吗?

五分钟内完成您的后端。

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