# 别人来运行这台机器

> 托管 Postgres 服务替你承担什么、又把什么留给你：从管理员角色和设置，到 I/O 上限、连接池、升级与故障切换。

---

LLMS 索引： [llms.txt](/llms.txt)

---

<span id="basics-chapter-34"></span>

图表显示，读取 [IOPS](/43-glossary/#iops) 已经整整一小时稳定在恰好 3,000。不是接近 3,000，而是恰好 3,000。延迟不断上升，应用请求超时，但仪表盘却显示数据库“健康”。没有东西坏掉。你买的是一块有上限的卷，而你的工作负载找到了这个上限。

这就是托管服务大多数时候的样子。服务商为操作系统打补丁、执行备份，并在凌晨 3 点提升备用实例。它不会替你选择索引，也不会调优 autovacuum。这些从来就不是服务器该做的事。它们是你的事，现在仍然是。这一章要讲的是两者之间的差异，先从究竟要把多少工作交给别人开始。

> [!NOTE] 截至 2026 年 10 月的示例
>
> 本章的服务商行为均引用各服务商截至 2026 年 10 月的文档；在据此构建方案前，请先检查链接。这里的服务只是用来说明机制，不是排名。[选择一个你能离开的服务商](/35-providers/#basics-chapter-35)一章保存了各厂商的具体数字。

## 能负担多少托管，就购买多少托管 {#buy-as-much-management-as-you-can-afford}

把 Postgres 运行好是一项全职技能，而大多数团队没有任何人能全职负责它。能恢复的备份、不会脑裂的故障切换、升级、清理、连接池，以及有人响应的值班电话：每一项都是一份工作，而且每份工作都要有人完成。唯一的问题是由谁完成。

对大多数团队，我们的答案是：在预算允许的范围内，尽可能多地把这些工作交给别人。

### 托管是一个连续谱，不是开关 {#management-is-a-spectrum-not-a-switch}

从你这边工作最多，到最少：

1. **自托管**在虚拟机或裸机上：完全的控制、完全的责任，以及值班电话。
2. **Kubernetes Operator**（[自我管理意味着你拥有超级用户和值班电话](/35-providers/#self-managing-gives-you-the-superuser-and-the-pager)）自动处理副本、备份和故障切换；但你仍然要运行它下面的集群。
3. **超大规模云厂商的托管服务**（[超大规模云厂商](/35-providers/#hyperscalers-win-on-breadth-and-cost-you-complexity)）负责机器运维、备份、备用实例和补丁更新，并给你一个管理员角色。
4. **专业托管服务**（[专业托管 Postgres](/35-providers/#specialists-buy-you-closer-support-and-fewer-regions)）：承担同样的平台工作，并配备一个应该懂 Postgres 内部机制的支持团队。用一个真实问题来检验这一点。
5. **托管加专家帮助**：购买最高级别的支持，或聘请兼职 DBA，让一个调优过上千个数据库的人来看看你的数据库。

没有哪一级适合所有人。每一级都会把工作从你的清单移到别人的清单上，同时带来相应的价格。

### 无论谁运行这台机器，有些工作仍然是你的 {#some-jobs-stay-yours-whoever-runs-the-box}

下面是本书所教工作的分工方式。超大规模云厂商和专业服务商两列结果相同，所以合并成了一列。

| 工作 | 自托管 | Operator | 托管服务 |
|---|---|---|---|
| [执行备份](/32-backups/#use-a-backup-tool) | 你 | 共同负责 | 服务商 |
| [测试恢复](/32-backups/#test-your-restores) | 你 | 你 | 你 |
| [时间点恢复](/32-backups/#point-in-time-recovery-is-replay-that-keeps-going) | 你 | 共同负责 | 共同负责 |
| [故障切换与高可用](/38-replication/#dont-hand-roll-failover) | 你 | 共同负责 | 共同负责 |
| [升级和操作系统打补丁](/32-backups/#take-minor-releases-promptly-rehearse-major-ones) | 你 | 共同负责 | 服务商（小版本），共同负责（大版本） |
| [清理和配置](/30-configuration/#autovacuums-defaults-suit-small-tables-and-slow-disks) | 你 | 你 | 你，在一个[控制台](#settings-live-in-a-parameter-group)中 |
| [连接池](/28-connections/#pool-in-the-app-and-in-front-of-the-database) | 你 | 共同负责 | 共同负责 |
| [监控和值班](/31-observability/#alert-only-on-what-needs-a-human-soon) | 你 | 你 | 共同负责 |
| [容量规划](/37-scale/#the-question-is-which-resource-runs-out-first) | 你 | 你 | 你 |
| [查询、索引和模式设计](/17-btree/#design-an-index-from-a-query-not-a-column) | 你 | 你 | 你 |

<span id="tbl-34-1"></span>

**Table 34-1.** 无论谁运行这台机器，有些工作仍然是你的


再读一遍最后一行。每一列写的都是“你”，而这正是本书大部分内容。

“共同负责”掩盖了真实的分工。服务商执行备份，但只有你能证明**你的**恢复满足**你的** RTO。服务商提升备用实例，但你的应用必须重新连接并重试。服务商会为坏掉的磁盘触发自己的告警，但你的值班范围仍然包括慢查询、锁队列和连接槽耗尽。

<span id="what-shared-means-job-by-job-and-what-expert-help-moves" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-what-shared-means-job-by-job-and-what-expert-help-moves" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：逐项说明“共同负责”意味着什么，以及专家帮助能移动哪些格子
>
> - **备份和 PITR：**Operator 会安排基础备份和 WAL 归档，而存储桶、生命周期规则和凭据由你负责。服务商负责归档和重放；你选择保留窗口，在窗口到期前发现错误，并把所有东西重新指向恢复所创建的新服务器。
> - **故障切换：**Operator 依赖你运行的 Kubernetes 集群；服务商的提升操作仍然需要你的应用[重新连接并重试](#failover-is-dns-and-your-pool-has-to-notice)。无论哪种方式，演练都由你安排。
> - **升级：**Operator 会把你选择的版本滚动发布到副本中（有些会自动处理大版本；请检查你使用的 Operator）。服务商会在你选择的窗口内打小版本补丁，并在大版本升级时运行 `pg_upgrade` 或[蓝绿升级](#bluegreen-is-the-logical-upgrade-limits-included)，而你要测试应用和扩展并运行 `analyze`。
> - **清理和配置：**服务商按实例大小而不是按最繁忙的数据表来设置默认值，并锁定部分设置。每张表的 autovacuum、`work_mem`、超时和日志记录仍然由你负责。
>
> 专家帮助只能移动几个格子，永远不会变成“服务商”：
>
> | 工作 | 仅托管服务 | 加支持合同 | 加兼职 DBA |
> |---|---|---|---|
> | 测试恢复 | 你 | 你 | 共同负责 |
> | 清理和配置调优 | 你 | 共同负责（按需提供建议） | 共同负责 |
> | 凌晨 3 点的事故 | 你，可向服务商升级求助 | 共同负责，按合同响应时间 | 共同负责，前提是其服务时间覆盖这一时段 |
> | 查询、索引和模式审查 | 你 | 你，或通过工单获取建议 | 共同负责 |
> 
> <span id="tbl-34-2"></span>
> 
> **Table 34-2.** 无论谁运行这台机器，有些工作仍然是你的


### 自我管理消耗的是人员，而不是服务器 {#self-managing-costs-people-not-servers}

虚拟机是便宜的部分。自我管理需要本书整个运维部分所涉及的专业能力，还需要一个由多人组成的值班轮换，每个人都必须能恢复备份并提升副本。一个专家加一张假期日历就是单点故障。它还带来了足以让公司倒闭的故障模式：没有人通过恢复验证过的备份（[GitLab，2017](/32-backups/#test-your-restores)），或者[两个主库同时接受写入](/38-replication/#the-old-primary-doesnt-know)。这些问题都不需要糟糕的工程师，只需要一项没人负责的工作。

用同一个单位计算两边的成本：

```text
  self-managed = servers + storage + backup storage
               + hours a month on the database  x  loaded hourly cost
               + the on-call rotation
               + chance of a bad incident  x  what it would cost you

  managed      = the provider's bill (compute, storage, I/O, support tier)
               + hours a month you still spend  x  loaded hourly cost
```

托管一侧的第二行永远不会变成零；表格最后几行的工作也永远不会离开你。但在服务器规模足够大、工作负载足够稳定并配有运维人员之前，人员成本通常远高于加价。计算你真正会花费的时间，而不是你希望花费的时间。

### 托管要付加价、控制权和退出成本 {#managed-costs-markup-control-and-an-exit}

服务本身有价格，本章剩下的内容会逐项说明它：加价，而且 I/O 的计费方式[有自己的故障模式](#the-bill-has-its-own-failure-modes)；更少的控制权（没有[超级用户](#there-is-no-superuser)，没有 `postgresql.conf`，没有[Shell](#you-cant-ssh-in-so-plan-your-logs)）；服务商自己的日程；以及[退出成本](/35-providers/#plan-the-exit-before-you-need-it)。对大多数团队来说，这仍然低于一套值班轮换。即便如此，也要把它写进账本。

### 托管不等于优化 {#managed-doesnt-mean-optimized}

服务商会让你的数据库持续运行，却不会让它变快。它不会注意到你的仪表盘查询为了返回 20 行而读取了 35,249 个页面，却会让你一键升级到更大的实例。更大的机器会掩盖查询问题，成本每月累积，而这个查询仍然排在列表顶端（[赢得更大的机器](/37-scale/#earn-the-bigger-box)）。

### 把运维工作交给服务商；设计工作留在自己手里 {#hand-a-provider-the-operations-keep-the-design-yourself}

具体来说，尤其是在没有专职数据库工程师时：让服务商负责备份、[PITR](/43-glossary/#pitr)、故障切换、打补丁和硬件。条件允许时再加专家帮助；让专家花一个小时处理你最慢的查询，往往比升级一次实例更划算。

然后把自己的专业能力投入服务商无法替你完成的地方：查询、索引和模式设计。这是本书的大部分内容，也是收益最大的地方：上面的仪表盘查询加上[第一个索引](/17-btree/#the-first-index-35249-pages-to-91)后，会从 35,249 个页面降到 91 个，而没有哪个服务档位会替你做到这一点。

只有在有具体理由、并且有人负责时才自我管理：例如规模大到加价足以支付一个团队、你能明确说出的控制需求、合规要求，或某个没有服务商提供的扩展。“纸面上更便宜”在把人员成本算进数学题之前都不算理由。并且这些选择都不是永久的；坚持使用社区 PostgreSQL 功能，你可以向任一方向迁移。

## 没有超级用户 {#there-is-no-superuser}

所有托管服务都不会授予 `SUPERUSER`，改为给你一个管理员角色：`rds_superuser`、`cloudsqlsuperuser`、`azure_pg_admin`。原因是超级用户可以读取服务器文件，并以服务器操作系统用户的身份运行程序，这等于拿到了服务商机器上的 Shell。

预计 `alter system`、`pg_read_file`、`copy ... to program`、C 函数和不受信任的扩展都会失败，所以要在事故发生前测试运行手册中的命令。给监控和值班账号授予预定义角色（`pg_monitor`、`pg_read_all_stats`、`pg_signal_backend`），永远不要给它们管理员密码。

<span id="the-admin-role-rebuilt-on-stock-postgres-and-each-providers-version" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-the-admin-role-rebuilt-on-stock-postgres-and-each-providers-version" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：在原生 Postgres 上重建管理员角色，以及各服务商的版本
>
> 实验中的 `mg_admin` 只有 `LOGIN CREATEROLE CREATEDB`，没有其他属性。`alter system`、`pg_read_file`、`pg_ls_dir`、`copy ... to program`、`create function ... language c`、`set log_min_duration_statement` 和 `create extension pg_visibility`（不受信任）都会以 `42501` 失败；`create extension hstore`（受信任）则成功。没有 `pg_read_all_stats` 时，另一个角色的会话会把它的查询显示为 `<insufficient privilege>`。就连本书用来查看原始页面的 `pageinspect`，默认也只有超级用户可用。
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > create role mg_admin login createrole createdb;
> >
> > set role mg_admin;
> >
> > select lab.prove(
> >   'reading a server file with pg_read_file is denied (42501)',
> >   try_sql($$ select pg_read_file('postgresql.conf') $$) = '42501');
> >
> > select lab.prove(
> >   'a function in the untrusted language C is denied (42501)',
> >   try_sql($$ create function mg_c() returns int language c as 'nothing', 'nothing' $$) = '42501');
> >
> > select lab.prove(
> >   'setting a superuser-only parameter for the session is denied (42501)',
> >   try_sql($$ set log_min_duration_statement = 0 $$) = '42501');
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > set role mg_watch;
> > select lab.prove(
> >   'without pg_read_all_stats, another role''s query shows as <insufficient privilege>',
> >   (select query = '<insufficient privilege>' and wait_event is null
> >    from pg_stat_activity where application_name = 'mg_sleeper'));
> >
> > select lab.prove(
> >   'without pg_signal_backend, cancelling another role''s query is denied (42501)',
> >   try_sql(format('select pg_cancel_backend(%s)',
> >     (select pid from pg_stat_activity where application_name = 'mg_sleeper'))) = '42501');
> > reset role;
> >
> > grant pg_read_all_stats, pg_signal_backend to mg_watch;
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)
>
> 截至 2026 年 10 月，各服务商的情况如下：AWS 的 [rds_superuser 页面](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Roles.rds_superuser.html)打印出 `CREATE ROLE postgres WITH LOGIN NOSUPERUSER INHERIT CREATEDB CREATEROLE ...`，并说明 `rds_superuser` 可以添加 RDS 的扩展、查看每个会话以及终止任意连接。[Cloud SQL](https://docs.cloud.google.com/sql/docs/postgres/users) 的客户“无法创建或访问带有超级用户属性的用户”；[AlloyDB](https://docs.cloud.google.com/alloydb/docs/database-users/about)授予 `alloydbsuperuser`。在 Azure 上，“只有 Microsoft 属于超级用户角色”（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/security/security-access-control)）。[Neon](https://neon.com/docs/manage/roles) 的 `neon_superuser` 持有 `pg_monitor` 和 `pg_signal_backend`，并带有 `ADMIN OPTION`；[Supabase](https://supabase.com/docs/guides/database/postgres/roles) 保留 `supabase_admin` 供自己使用。接下来，管理员角色应根据[应用永远不以 postgres 连接](/33-security/#the-app-never-connects-as-postgres)一节创建所有者角色、迁移角色和应用角色。

## 设置位于参数组中 {#settings-live-in-a-parameter-group}

没有超级用户也意味着没有配置文件。你不能编辑 `postgresql.conf`；服务商会从控制台对象生成它（[托管服务说明](/30-configuration/#on-managed-services-the-settings-that-matter-still-move)）。

### 静态意味着重启，而控制台不会替你完成重启 {#static-means-restart-and-the-console-wont-do-it-for-you}

[需要重启](/30-configuration/#context-decides-reload-or-restart)的设置就是“静态”设置。在 RDS 上，静态变更会停留在 `pending-reboot` 状态，而该状态“不会在下一次维护窗口中自动重启”（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/parameter-groups-overview.html)），因此它可能等上几个月，最后与一次无关的重启一起生效。把静态变更集中安排在你选择的窗口内。

### 默认值不是本书中的默认值 {#the-defaults-are-not-the-books-defaults}

服务商按实例大小而不是[25% 规则](/30-configuration/#shared_buffers-starts-at-25-of-ram-not-80)来配置内存：Cloud SQL 将 `shared_buffers` 设置为内存的三分之一，Aurora 大约设置为三分之二。在假设之前，先读取 `pg_settings`。

### 每个角色的设置才是可迁移的工具 {#per-role-settings-are-the-portable-tool}

`alter role ... set` 不需要控制台，而且可以随你在不同服务商之间迁移。在迁移脚本中按角色保留超时、`work_mem` 覆盖值和 `search_path`；把控制台留给内存、日志和 autovacuum。

<span id="restarts-on-the-other-consoles-and-the-settings-a-role-cant-take" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-restarts-on-the-other-consoles-and-the-settings-a-role-cant-take" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：其他控制台中的重启，以及角色无法接管的设置
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > select lab.prove(
> >   'shared_buffers, max_connections and shared_preload_libraries need a restart (context postmaster)',
> >   (select bool_and(context = 'postmaster') from pg_settings
> >    where name in ('shared_buffers', 'max_connections', 'shared_preload_libraries'))
> >   and (select count(*) from pg_settings
> >        where name in ('shared_buffers', 'max_connections', 'shared_preload_libraries')) = 3);
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)
>
> [Cloud SQL 的配置标志表](https://docs.cloud.google.com/sql/docs/postgres/flags)会为每个配置标志标记“是否需要重启？”，并将 `shared_buffers` 设置为“实例 VM 总内存的三分之一”。[Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Reference.ParameterGroups.html)会把它设置为 `SUM(DBInstanceClassMemory/12038,-50003)` 个 8 KB 页面。Azure 表示 `shared_preload_libraries`“需要服务器重启”（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/extensions/how-to-load-libraries)）。RDS 的默认参数组不能编辑；先创建一个自定义参数组。在 `pg_settings` 中，`source` 表示某个值是由服务商设置的，还是由你设置的。
>
> 标记为 `superuser` 的设置，在有人执行 `GRANT SET ON PARAMETER`（PostgreSQL 15+）之前，不能按角色设置；这是服务商可以用来委托权限的钩子。在原生 Postgres 上，`alter role mg_app set statement_timeout = '5s'` 可以成功，而对 `log_min_duration_statement` 执行同样操作会以 `42501` 失败。
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > select lab.prove(
> >   'ALTER ROLE ... SET works for an ordinary parameter (statement_timeout)',
> >   try_sql($$ alter role mg_app set statement_timeout = '5s' $$) = 'ok');
> >
> > select lab.prove(
> >   'ALTER ROLE ... SET fails for a superuser-only parameter (log_min_duration_statement): 42501',
> >   try_sql($$ alter role mg_app set log_min_duration_statement = 250 $$) = '42501');
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)

## 扩展来自允许列表 {#extensions-come-from-an-allowlist}

你只能 `create extension` 服务商已经编译并允许的扩展，而脚注和列表本身同样重要：版本可能落后、配置标志可能要求重启、后台工作进程可能缺失，或者功能只在付费计划中提供。在围绕某个扩展设计方案前，先检查它（[每个扩展的用途](/16-extensions/#basics-chapter-16)），并在每次大版本升级后运行 `alter extension ... update`；扩展不会自动升级。

### 服务商会在 CREATE EXTENSION 上再加一道门 {#the-provider-adds-its-own-gate-on-create-extension}

从 PostgreSQL 13 开始，**受信任**的扩展可以由拥有数据库 `CREATE` 权限的非超级用户安装。不受信任的扩展需要超级用户，这里意味着服务商，或者根本没人能装。服务商还会加上自己的门槛，而且默认值正好相反：RDS 允许所有可用扩展，Azure 则要先把扩展列入允许列表，默认什么都不允许。

<span id="five-extensions-across-six-services-with-the-fine-print" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-five-extensions-across-six-services-with-the-fine-print" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：六个服务中的五个扩展，以及其中的细则
>
> | 扩展 | RDS | Aurora | Cloud SQL | AlloyDB | Azure | Neon |
> |---|---|---|---|---|---|---|
> | `pg_stat_statements` | 是 | 是 | 是 | 是 | 是 | 是 |
> | `vector` | 0.8.2 | 0.8.2 | 0.8.5 | 0.8.2 | 0.8.2 | 是 |
> | `pg_partman` | 5.4.3 | 5.4.3 | 5.4.3¹ | 5.4.3 | 5.4.3 | 5.1.0 |
> | `pg_repack` | 1.5.3 | 1.5.3 | 1.5.2 | 1.5.3 | 1.5.3 | 付费² |
> | `pg_hint_plan` | 1.8.0 | 1.8.0 | 配置标志 | 配置标志 | 1.8.0 | 1.8.0 |
> 
> <span id="tbl-34-3"></span>
> 
> **Table 34-3.** 服务商会在 CREATE EXTENSION 上再加一道门

>
> PostgreSQL 18 上的版本（RDS 和 Aurora 为 18.6）。¹ 没有后台工作进程。² 仅限付费计划。来源，2026 年 10 月：[RDS](https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLReleaseNotes/postgresql-extensions.html)、[Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Extensions.html)、[Cloud SQL](https://docs.cloud.google.com/sql/docs/postgres/extensions)、[AlloyDB](https://docs.cloud.google.com/alloydb/docs/reference/extensions)、[Azure](https://learn.microsoft.com/en-us/azure/postgresql/extensions/concepts-extensions-versions)、[Neon](https://neon.com/docs/extensions/pg-extensions)；[Supabase](https://supabase.com/docs/guides/database/extensions) 列出了上表中除 `pg_hint_plan` 以外的所有扩展。Cloud SQL 的 `pg_partman`“不包含后台工作进程”（改为从 `pg_cron` 调用维护任务）；`pg_hint_plan` 隐藏在 `cloudsql.enable_pg_hint_plan`（需要重启）或 `alloydb.enable_pg_hint_plan` 后面；Neon 的 `pg_repack`“仅在付费 Neon 计划中可用”；AlloyDB 无法启用 autoprewarm，因为你不能配置 shared_preload_libraries，所以[autoprewarm 技巧](/05-buffers/#autoprewarm-restores-the-last-cache)无法使用。
>
> `pg_available_extension_versions` 会为每个扩展显示 `trusted`。作为实验中的管理员角色，`create extension hstore`（受信任）可以成功，而 `create extension pg_visibility`（不受信任）会以 `42501` 失败。
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > select lab.prove(
> >   'the admin role can create a trusted extension (hstore)',
> >   try_sql($$ create extension hstore $$) = 'ok');
> >
> > select lab.prove(
> >   'the admin role cannot create an untrusted extension (pg_visibility): 42501',
> >   try_sql($$ create extension pg_visibility $$) = '42501');
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)
>
> RDS 的 `rds.allowed_extensions` 默认值为 `'*'`，并且是动态设置（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.FeatureSupport.Extensions.html)）；Azure 要求先在 `azure.extensions` 中“把扩展加入允许列表”（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/extensions/how-to-allow-extensions)）；在 Cloud SQL 上，只有 `cloudsqlsuperuser` 成员可以创建扩展。AWS 说明扩展版本“不会在引擎版本升级流程中自动升级”。

## 现在，卷成了你的瓶颈 {#the-volume-is-your-bottleneck-now}

在托管服务上，你很少会先耗尽 CPU。你会先耗尽 I/O，因为数据库位于[网络块存储](/03-latency/#iops-are-capped-by-reads-in-flight-not-by-what-you-paid)上，而它有预置上限；这些数字比人们想象的更小：400 GiB 以下的 RDS gp3 卷基线是 3,000 IOPS，128 GiB 的 Azure Premium SSD 是 500 IOPS。

### 两个上限，你会撞上其中较低的那个 {#two-ceilings-and-you-hit-the-lower-one}

磁盘有一个上限，实例也有一个上限，而你得到的是其中较小的那个。

```text
  what you get = min( volume IOPS,  instance IOPS cap )

  ┌──────────────┐   instance cap      ┌──────────────────┐
  │   instance   │ ──────────────────▶ │   network volume │
  │  2 vCPU      │   (often burstable) │  16,000 IOPS     │
  └──────────────┘                     └──────────────────┘
        a 16,000 IOPS volume behind a small instance
        delivers what the small instance can push
```

在小实例后面接一块更快的卷不会带来任何收益，而小实例的[突发额度](/03-latency/#burst-credits-run-out-in-the-middle-of-the-job)即使在流量没有变化时也会耗尽。

### I/O 被节流时，会表现为 IOPS 持平而延迟上升 {#throttled-io-shows-as-flat-iops-and-rising-latency}

在 Postgres 内部，活动会话会等待 `IO`，主要是 `DataFileRead`（某个页面不在共享缓冲区中）；可以多采样几次[等待事件](/31-observability/#sample-waits-to-see-where-the-time-goes)。在服务商的指标中，它就是本章开头所说的情形：IOPS 顶在某个数字上，而延迟和队列深度持续上升。然后按本书的顺序修复：少读页面，给工作集更多内存，最后才购买 IOPS。

<span id="what-each-provider-sells-and-the-metrics-that-show-the-ceiling" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-what-each-provider-sells-and-the-metrics-that-show-the-ceiling" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：各服务商卖什么，以及哪些指标显示了上限
>
> 截至 2026 年 10 月：**RDS gp3** 在 20 到 399 GiB 范围内提供 3,000 IOPS 和 125 MiB/s；从 400 GiB 开始会把四个卷条带化，提供 12,000 IOPS 和 500 MiB/s；gp2 每 GiB 提供 3 IOPS，并可突发到 3,000（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)）。**Azure Premium SSD** 在 128 GiB 时提供 500 IOPS，并可“突发到最高 3,500”，512 GiB 时提供 2,300；v2 在任何容量都从 3,000 开始（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/compute-storage/concepts-storage)）。**Cloud SQL** 的 SSD IOPS 随机器类型和容量变化，并且“不能配置这些值”；Hyperdisk Balanced 在 20 到 256 GB 范围内默认提供 4,000 IOPS（[Google](https://docs.cloud.google.com/sql/docs/postgres/storage-options-overview)）。**Supabase** 说得很清楚：“有效 IOPS 取计算规格支持的 IOPS 与磁盘预置 IOPS 中较低者”，其 Micro 计算规格的基线是 500 IOPS（[Supabase](https://supabase.com/docs/guides/platform/compute-and-disk)）。AWS 警告某个实例“可能无法充分利用预置的 IOPS”；Microsoft 说 VM 类型“也有 IOPS 限制”；Google 说“机器类型决定上限”。
>
> | 信号 | RDS（CloudWatch） | Azure |
> |---|---|---|
> | I/O 被顶在上限 | `ReadIOPS`、`WriteIOPS`、`ReadThroughput` | `disk_iops_consumed_percentage` |
> | 请求排队 | `DiskQueueDepth`、`ReadLatency` | `disk_queue_depth` |
> | I/O 突发额度正在耗尽 | `BurstBalance`（gp2）、`EBSIOBalance%` | `disk_iops_consumed_percentage` 接近 100 |
> | CPU 突发额度正在耗尽 | T 类实例的 `CPUCreditBalance` | 可突发实例的 `cpu_credits_remaining` |
> 
> <span id="tbl-34-4"></span>
> 
> **Table 34-4.** I/O 被节流时，会表现为 IOPS 持平而延迟上升

>
> 名称来自 [AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) 和 [Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/monitor/concepts-monitoring)；AWS 的 [`IO:DataFileRead` 页面](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/wait-event.iodatafileread.html)描述了同一种等待。`EBSIOBalance%` 是实例的突发额度池：gp3 卷没有突发能力，但它前面的实例可能有。

## 分离式存储改变了部分规则 {#disaggregated-storage-changes-some-rules}

Aurora、AlloyDB 和 Neon 保留 Postgres 在缓冲区缓存以上的部分，并替换缓存以下的存储层：服务器把 WAL 发送到一个构建页面的分布式存储服务（[这会如何改变基准测试](/35-providers/#storage-decides-the-benchmark)）。Aurora 和 AlloyDB 跳过整页写入，因此[检查点之后的 WAL 峰值](/08-wal/#the-first-change-after-a-checkpoint-logs-the-whole-page)会消失，所以不要照搬检查点调优指南。副本共享存储，而不是复制存储；I/O 可能成为一项费用；缓存未命中意味着一次网络读取。行版本、清理、膨胀以及规划器对页面的计数都完全不变。

<span id="what-aurora-alloydb-and-neon-say-about-their-storage" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-what-aurora-alloydb-and-neon-say-about-their-storage" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：Aurora、AlloyDB 和 Neon 如何描述自己的存储
>
> 截至 2026 年 10 月：
>
> - **Aurora** 在三个可用区之间把写入复制到“六个存储节点”；副本读取同一个卷，延迟“通常远低于 100 毫秒”（[存储](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.StorageReliability.html)、[复制](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Replication.html)、[HA](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html)）。它的参数参考中没有 `checkpoint_timeout`、`max_wal_size` 或 `full_page_writes`。Aurora Standard 按“每 100 万次 I/O 操作请求”收费；I/O-Optimized 不按此收费，并且当“你的 I/O 支出占 Aurora 数据库总支出的 25% 或更多”时是“最佳选择”。
> - **AlloyDB** 把 WAL 写入“低延迟的区域日志存储服务”，因此“无需在数据库层执行检查点”，也不需要整页写入（[Google Cloud 博客，2022](https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storage)）。
> - **Neon**“在达到法定人数的 safekeeper 通过 Paxos 协议确认 WAL 记录后提交”，而 pageserver 会按请求重建页面（[Neon](https://neon.com/docs/introduction/architecture-overview)）。缩容到零的计算实例唤醒时，缓冲区是空的（[相关章节](/35-providers/#serverless-platforms-optimize-for-the-developers-day)给出了具体时间）。读己之写仍然[需要小心](/38-replication/#after-a-write-a-replica-can-show-the-old-value)：延迟很小，但不是零。

## 服务商仪表盘是等待事件采样器 {#provider-dashboards-are-wait-event-samplers}

每个服务商的性能仪表盘都是[等待事件画像](/31-observability/#sample-waits-to-see-where-the-time-goes)，只是被采样并保存下来。用同样的方式阅读它：IO 条带意味着页面正在从存储中读取，Lock 条带意味着一个[锁队列](/25-concurrency/#basics-chapter-25)。修复点仍然在查询中。

### “CPU”不是等待事件 {#cpu-is-not-a-wait-event}

每个仪表盘都有 CPU 条带，而 Postgres 没有名为 CPU 的等待事件。这个条带表示没有等待事件、处于 `active` 状态的会话：它们正在运行，或在等待操作系统调度器，因此数值可以高于核心数。

<span id="what-each-dashboard-samples-per-its-docs" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-what-each-dashboard-samples-per-its-docs" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：根据文档，各仪表盘采样的内容
>
> > [!EXAMPLE]- 动手验证 · lab `33-managed`
> >
> > ```sql
> > select lab.prove(
> >   'PostgreSQL has no wait event called CPU (pg_wait_events, PostgreSQL 17+)',
> >   not exists (select 1 from pg_wait_events where name ilike 'cpu'));
> >
> > select lab.prove(
> >   'a CPU-bound session samples as active with no wait event (at least 8 of 10 samples)',
> >   (select count(*) filter (where state = 'active' and wait_event is null) >= 8
> >    from mg_samples));
> > ```
> >
> > [完整实验脚本](https://www.flagon.io/books/eight-kilobytes/lab/33-managed.sql)
>
> 截至 2026 年 10 月：RDS Database Insights 每秒“采样同时运行查询的会话数量”，并将 CPU 定义为“在 CPU 上活动或正在等待 CPU”（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PerfInsights.Overview.ActiveSessions.html)）。Cloud SQL Query Insights 只有 Enterprise Plus 才显示等待事件（[Google](https://docs.cloud.google.com/sql/docs/postgres/using-query-insights)），Google 还指出 CPU 负载“可以超过核心数上限线”。AlloyDB 将“CPU 和 CPU Wait”、“IO Wait”和“Lock Wait”分开显示（[Google](https://docs.cloud.google.com/alloydb/docs/using-query-insights)）。Azure Query Store 会采样等待，但在 Burstable 上不会采样，因为“会导致性能问题”（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/monitor/concepts-query-store)）。仪表盘有保留期限；可观测性一章的 SQL 在任何服务上都能运行。

## 托管连接池要么固定会话，要么泄露状态 {#a-managed-pooler-either-pins-sessions-or-leaks-state}

在事务模式中，会话状态要么会出问题，要么必须被跟踪（[事务池模式会破坏会话状态](/28-connections/#transaction-pooling-breaks-session-state)），而托管连接池会选择其中一种处理方式。

RDS Proxy 会**固定会话**：一旦客户端改变会话状态（`SET`、预处理语句，甚至 `nextval`），它就会一直获得自己的服务器连接，直到断开连接。这样做是正确的，但你付费购买的多路复用就没有了；如果 ORM 在从连接池取出连接时设置一个变量，就会固定每个连接。Cloud SQL、Azure、Neon 和 Supabase 上的[PgBouncer](/43-glossary/#pgbouncer)式连接池**不跟踪** SQL 级会话状态（`SET`、`PREPARE`、咨询锁）：多路复用完整保留，但状态会静默泄露。有些连接池会在服务商启用后跟踪协议级预处理语句，也就是大多数驱动发送的那一种。

无论是哪一种，都要知道自己使用的模式，并使用 `SET LOCAL` 和按角色设置。

<span id="each-managed-poolers-rules-as-documented" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-each-managed-poolers-rules-as-documented" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：各托管连接池的规则（按文档整理）
>
> 截至 2026 年 10 月：**RDS Proxy** 还会在游标、`LISTEN`、临时表、会话级咨询锁和超过 16 KB 的语句上固定会话；如果连接池用 `DISCARD ALL` 重置，则所有连接都会被固定；请观察 `DatabaseConnectionsCurrentlySessionPinned`（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-pinning.html)）。**Cloud SQL** 的连接池需要 Enterprise Plus，启用它“会导致数据库重启”；事务模式“不支持”`SET`、`LISTEN` 和 `PREPARE`（[Google](https://docs.cloud.google.com/sql/docs/postgres/managed-connection-pooling)）。**Azure** 的 PgBouncer 1.25.2 将 `pgbouncer.max_prepared_statements` 设置为 0，“不支持 Burstable”，并且每次故障切换都会重启（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/connectivity/concepts-pgbouncer)）。**Neon** 在 `-pooler` 主机名后运行 PgBouncer（[Neon](https://neon.com/docs/connect/connection-pooling)）；**Supabase** 的事务模式“不支持预处理语句”（[Supabase](https://supabase.com/docs/guides/database/connecting-to-postgres)）。

## 备份是快照加 WAL，而恢复会创建新服务器 {#backups-are-snapshots-plus-wal-and-restores-make-new-servers}

每项服务都按照[备份一章](/32-backups/#point-in-time-recovery-is-replay-that-keeps-going)所描述的方式构建时间点恢复：快照加归档 WAL。按钮确实有效。但它不会提醒你保留期是固定窗口（各服务商的窗口见[超级用户访问和保留规则会排除哪些服务](/35-providers/#superuser-access-and-retention-rule-services-out)），最后几分钟的 WAL 可能还没发送出去，或者删除实例时可能会把自动备份一并删除。

### 恢复时间比按钮提示的更长 {#restore-time-is-longer-than-the-button-suggests}

托管恢复会创建**一台新服务器**，因此应用、连接池、密钥和防火墙规则都必须跟着它走。**数据可能还没有全部出现**：RDS 会惰性加载恢复快照中的数据块，所以最初几小时的速度可能只有对象存储的速度。并且**缓存是冷的**。*20 分钟后可用*可能意味着*四小时后才能投入生产*。请对生产规模的数据[计时恢复](/32-backups/#test-your-restores)，把预热也算进去，并在服务商账户无法删除的位置保留自己的 `pg_dump`。

<span id="rpo-deletion-and-lazy-loading-per-provider" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-rpo-deletion-and-lazy-loading-per-provider" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：各服务商的 RPO、删除行为和惰性加载
>
> 截至 2026 年 10 月：
>
> - **RDS**“每五分钟将事务日志上传到 Amazon S3”，并且除非另有指定，“所有自动备份都会随 DB 实例一起删除”（[PITR](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PIT.html)、[备份](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html)）。请求尚未加载的恢复数据块时，会“立即从 Amazon S3 下载请求的数据”；AWS 建议扫描整张表来预热热点表（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RestoreFromSnapshot.html)）。
> - **Cloud SQL：**“时间点恢复总是会创建一个新实例”（[Google](https://docs.cloud.google.com/sql/docs/postgres/backup-recovery/pitr)）；对于已删除的实例，请在“四天内”联系支持团队（[备份](https://docs.cloud.google.com/sql/docs/postgres/backup-recovery/backups)）。
> - **Azure：**“延迟 RPO……可能最长达到五分钟”（[Microsoft](https://learn.microsoft.com/en-us/azure/postgresql/backup-restore/concepts-backup-restore)）。

## 蓝绿升级就是逻辑升级，限制也会一并到来 {#bluegreen-is-the-logical-upgrade-limits-included}

原地升级会在停机状态下运行 `pg_upgrade`，并且可能带来意外：Cloud SQL 会“删除实例上所有现有的逻辑复制槽”，而 RDS 只能通过恢复旧快照回退。低停机路径是[逻辑复制升级](/32-backups/#logical-replication-shrinks-upgrade-downtime-to-the-cutover)，RDS 将其作为 Blue/Green Deployments（蓝绿部署）出售。它的限制也会一起出现（冻结 DDL、每张表都要有主键），所以要在克隆上进行演练，让演练找出那张没有主键的数据表。

<span id="the-in-place-and-bluegreen-fine-print" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-the-in-place-and-bluegreen-fine-print" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：原地升级和蓝绿升级的细则
>
> 原地升级：[RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.html) 表示“直到升级完成前，你都会经历中断”；[Cloud SQL](https://docs.cloud.google.com/sql/docs/postgres/upgrade-major-db-version-inplace)；[Azure](https://learn.microsoft.com/en-us/azure/postgresql/configure-maintain/concepts-major-version-upgrade)要求“在每个数据库中运行 ANALYZE 命令”。RDS 蓝绿部署对支持的大版本升级使用逻辑复制（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-replication-type.html)），表示切换“通常不到一分钟”；根据其[注意事项](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-considerations.html)：蓝环境上的 DDL 会使复制进入降级状态并迫使重建（禁用会自行运行 DDL 的 `pg_partman`），序列只在切换时同步，大对象和不记录日志的表不会复制，而单线程应用变更时可能跟不上繁忙的蓝环境。

## 故障切换依赖 DNS，你的连接池必须注意到 {#failover-is-dns-and-your-pool-has-to-notice}

故障切换是你付费购买服务商服务的主要原因。文档中给出的时间大多落在 30 到 120 秒之间，而所有连接都会断开。随后，大多数服务会让一个 DNS 名称指向新的主库（Cloud SQL 和 AlloyDB 则保留静态 IP）；无论哪种方式，你的连接池都会继续持有失效连接，直到发现它们失效。你这边要履行的合同义务是：重新连接，而不是缓存旧地址（AWS 建议 JVM DNS TTL“不超过 60 秒”）；重试整个事务；检查逻辑复制槽是否保留；并且要有意地、在工作时间内、按计划执行故障切换演练（[要问的三个问题](/38-replication/#dont-hand-roll-failover)）。

<span id="documented-failover-times-and-the-fine-print-that-stretches-them" class="pgbasics-deep-dive-anchor" data-td-print-local></span><span id="basics-34-documented-failover-times-and-the-fine-print-that-stretches-them" class="pgbasics-deep-dive-anchor"></span>

> [!DETAILS]- 深入阅读：文档中的故障切换时间，以及会拉长它们的细节
>
> | 部署方式 | 文档给出的典型故障切换时间 |
> |---|---|
> | RDS Multi-AZ 实例 | 通常为 60 到 120 秒 |
> | RDS Multi-AZ 集群（两个可读备用实例） |“通常少于 35 秒”|
> | 带副本的 Aurora |“少于 60 秒，通常少于 30 秒”|
> | 不带副本的 Aurora |“通常少于 10 分钟”|
> | Cloud SQL HA | 不可用，“大约六十秒” |
> | AlloyDB HA | 检测“最多 30 秒”，然后“通常少于 30 秒” |
> | Azure 可用区冗余 HA |“60 到 120 秒内且零数据丢失”|
> 
> <span id="tbl-34-5"></span>
> 
> **Table 34-5.** 故障切换依赖 DNS，你的连接池必须注意到

>
> 来源，2026 年 10 月：[RDS Multi-AZ](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.Failover.html)、[RDS 集群](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts-failover.html)、[Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html)、[Cloud SQL](https://docs.cloud.google.com/sql/docs/postgres/high-availability)、[AlloyDB](https://docs.cloud.google.com/alloydb/docs/high-availability)、[Azure](https://learn.microsoft.com/en-us/azure/postgresql/high-availability/concepts-high-availability)。AWS：“大型事务或漫长的恢复过程可能增加故障切换时间”。Microsoft：备用实例“通常以 40 MB/s 的速度恢复 WAL 文件”，并且从 PostgreSQL 17 开始，只要配置正确，向备用实例同步复制槽就由原生功能提供。RDS 和 Azure 会移动 DNS；Cloud SQL 和 AlloyDB 保留静态 IP。

## 不能 SSH 登录，所以要规划日志 {#you-cant-ssh-in-so-plan-your-logs}

没有 Shell 就没有 `tail -f`。日志存放在服务商指定的位置，并且只保留服务商规定的时长：RDS 默认保留日志文件 3 天，最多 7 天（[AWS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.PostgreSQL.overview.parameter-groups.html)）。第一天就设置[值得保留的日志](/30-configuration/#cheap-logging-answers-most-3-am-questions)，并把它发送到你能控制的地方，否则在事故复盘前证据就会消失。

## 账单也有自己的故障模式 {#the-bill-has-its-own-failure-modes}

托管定价会把性能问题变成账单项目，所以慢查询会出现两次：一次在仪表盘上，一次在发票上。在 Aurora Standard 上，每一次不必要的顺序扫描都会计入 I/O 费用。AWS 在可用区之间收取“每 GB 进出各 0.01 美元”（[AWS](https://aws.amazon.com/rds/pricing/)，2026 年 10 月），因此应用从另一个可用区的副本读取时，每个结果集都要付费。

最昂贵的陷阱，是为了慢查询而升级的实例：[关系一章](/14-relationships/#basics-chapter-14)中的删除操作，在没有索引时会读取 `events` 的全部 35,173 个页面，有索引时只读三个页面。RAM 会掩盖问题；索引可以修复它，而且每月不增加任何费用。

## 本章要点 {#the-short-version}

- 尽可能购买你能负担的托管服务，把自己的专业能力投入查询、索引和模式设计。只有在有具体理由、并且有人负责时才自我管理。
- 托管不等于优化。用人员时间而不只是账单来比较两种方案。
- 没有超级用户：给监控和值班账号授予预定义角色，不要给它们管理员密码。
- 静态设置要等你安排的重启；把按角色设置写入迁移脚本。围绕扩展设计方案前，先检查扩展列表中的脚注。
- 你得到的是卷和实例 I/O 上限中较低的那个。IOPS 持平且存在 `DataFileRead` 等待时，先减少读取的页面，最后才购买 IOPS。
- 知道你的连接池是在固定会话，还是会泄露状态。对真实恢复计时，在克隆上演练升级，并让应用在故障切换期间重新连接和重试。

## 延伸阅读 {#further-reading}

- [Understanding the rds_superuser role](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Roles.rds_superuser.html)，AWS 文档。RDS 管理员角色的确切属性及其能力。
- [Avoiding pinning an RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-pinning.html)，AWS 文档。会固定 PostgreSQL 会话的完整事项清单，以及查看方法。
- [RDS Blue/Green Deployments considerations](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-considerations.html)，AWS 文档。蓝绿大版本升级中会遇到的每一项逻辑复制限制。
- [PgBouncer in Azure Database for PostgreSQL](https://learn.microsoft.com/en-us/azure/postgresql/connectivity/concepts-pgbouncer)，Microsoft 文档。托管事务连接池的设置和限制说明。
- [Cloud SQL high availability](https://docs.cloud.google.com/sql/docs/postgres/high-availability)，Google Cloud 文档。区域备用实例如何执行故障切换，以及故障切换期间应用会看到什么。
- [Neon architecture overview](https://neon.com/docs/introduction/architecture-overview)，Neon 文档。Safekeeper、pageserver 以及基于法定人数的提交的含义。
- [Predefined roles](https://www.postgresql.org/docs/18/predefined-roles.html)，PostgreSQL 文档。每个内置角色授予什么权限，以及如何在任何服务商上构建最小权限账号。
