跳转到主要内容

34 别人来运行这台机器

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

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

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

截至 2026 年 10 月的示例

本章的服务商行为均引用各服务商截至 2026 年 10 月的文档;在据此构建方案前,请先检查链接。这里的服务只是用来说明机制,不是排名。选择一个你能离开的服务商一章保存了各厂商的具体数字。

能负担多少托管,就购买多少托管

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

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

托管是一个连续谱,不是开关

从你这边工作最多,到最少:

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

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

无论谁运行这台机器,有些工作仍然是你的

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

工作自托管Operator托管服务
执行备份你共同负责服务商
测试恢复你你你
时间点恢复你共同负责共同负责
故障切换与高可用你共同负责共同负责
升级和操作系统打补丁你共同负责服务商(小版本),共同负责(大版本)
清理和配置你你你,在一个控制台中
连接池你共同负责共同负责
监控和值班你你共同负责
容量规划你你你
查询、索引和模式设计你你你
表 34-1 无论谁运行这台机器,有些工作仍然是你的

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

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

深入阅读:逐项说明“共同负责”意味着什么,以及专家帮助能移动哪些格子
  • **备份和 PITR:**Operator 会安排基础备份和 WAL 归档,而存储桶、生命周期规则和凭据由你负责。服务商负责归档和重放;你选择保留窗口,在窗口到期前发现错误,并把所有东西重新指向恢复所创建的新服务器。
  • **故障切换:**Operator 依赖你运行的 Kubernetes 集群;服务商的提升操作仍然需要你的应用重新连接并重试。无论哪种方式,演练都由你安排。
  • **升级:**Operator 会把你选择的版本滚动发布到副本中(有些会自动处理大版本;请检查你使用的 Operator)。服务商会在你选择的窗口内打小版本补丁,并在大版本升级时运行 pg_upgrade 或蓝绿升级,而你要测试应用和扩展并运行 analyze。
  • **清理和配置:**服务商按实例大小而不是按最繁忙的数据表来设置默认值,并锁定部分设置。每张表的 autovacuum、work_mem、超时和日志记录仍然由你负责。

专家帮助只能移动几个格子,永远不会变成“服务商”:

工作仅托管服务加支持合同加兼职 DBA
测试恢复你你共同负责
清理和配置调优你共同负责(按需提供建议)共同负责
凌晨 3 点的事故你,可向服务商升级求助共同负责,按合同响应时间共同负责,前提是其服务时间覆盖这一时段
查询、索引和模式审查你你,或通过工单获取建议共同负责
表 34-2 无论谁运行这台机器,有些工作仍然是你的

自我管理消耗的是人员,而不是服务器

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

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

  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

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

托管要付加价、控制权和退出成本

服务本身有价格,本章剩下的内容会逐项说明它:加价,而且 I/O 的计费方式有自己的故障模式;更少的控制权(没有超级用户,没有 postgresql.conf,没有Shell);服务商自己的日程;以及退出成本。对大多数团队来说,这仍然低于一套值班轮换。即便如此,也要把它写进账本。

托管不等于优化

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

把运维工作交给服务商;设计工作留在自己手里

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

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

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

没有超级用户

所有托管服务都不会授予 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),永远不要给它们管理员密码。

深入阅读:在原生 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,默认也只有超级用户可用。

动手验证 · lab 33-managed
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');

完整实验脚本

动手验证 · lab 33-managed
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;

完整实验脚本

截至 2026 年 10 月,各服务商的情况如下:AWS 的 rds_superuser 页面打印出 CREATE ROLE postgres WITH LOGIN NOSUPERUSER INHERIT CREATEDB CREATEROLE ...,并说明 rds_superuser 可以添加 RDS 的扩展、查看每个会话以及终止任意连接。Cloud SQL 的客户“无法创建或访问带有超级用户属性的用户”;AlloyDB授予 alloydbsuperuser。在 Azure 上,“只有 Microsoft 属于超级用户角色”(Microsoft)。Neon 的 neon_superuser 持有 pg_monitor 和 pg_signal_backend,并带有 ADMIN OPTION;Supabase 保留 supabase_admin 供自己使用。接下来,管理员角色应根据应用永远不以 postgres 连接一节创建所有者角色、迁移角色和应用角色。

设置位于参数组中

没有超级用户也意味着没有配置文件。你不能编辑 postgresql.conf;服务商会从控制台对象生成它(托管服务说明)。

静态意味着重启,而控制台不会替你完成重启

需要重启的设置就是“静态”设置。在 RDS 上,静态变更会停留在 pending-reboot 状态,而该状态“不会在下一次维护窗口中自动重启”(AWS),因此它可能等上几个月,最后与一次无关的重启一起生效。把静态变更集中安排在你选择的窗口内。

默认值不是本书中的默认值

服务商按实例大小而不是25% 规则来配置内存:Cloud SQL 将 shared_buffers 设置为内存的三分之一,Aurora 大约设置为三分之二。在假设之前,先读取 pg_settings。

每个角色的设置才是可迁移的工具

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

深入阅读:其他控制台中的重启,以及角色无法接管的设置
动手验证 · lab 33-managed
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);

完整实验脚本

Cloud SQL 的配置标志表会为每个配置标志标记“是否需要重启?”,并将 shared_buffers 设置为“实例 VM 总内存的三分之一”。Aurora会把它设置为 SUM(DBInstanceClassMemory/12038,-50003) 个 8 KB 页面。Azure 表示 shared_preload_libraries“需要服务器重启”(Microsoft)。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 失败。

动手验证 · lab 33-managed
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');

完整实验脚本

扩展来自允许列表

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

服务商会在 CREATE EXTENSION 上再加一道门

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

深入阅读:六个服务中的五个扩展,以及其中的细则
扩展RDSAuroraCloud SQLAlloyDBAzureNeon
pg_stat_statements是是是是是是
vector0.8.20.8.20.8.50.8.20.8.2是
pg_partman5.4.35.4.35.4.3¹5.4.35.4.35.1.0
pg_repack1.5.31.5.31.5.21.5.31.5.3付费²
pg_hint_plan1.8.01.8.0配置标志配置标志1.8.01.8.0
表 34-3 服务商会在 CREATE EXTENSION 上再加一道门

PostgreSQL 18 上的版本(RDS 和 Aurora 为 18.6)。¹ 没有后台工作进程。² 仅限付费计划。来源,2026 年 10 月:RDS、Aurora、Cloud SQL、AlloyDB、Azure、Neon;Supabase 列出了上表中除 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 技巧无法使用。

pg_available_extension_versions 会为每个扩展显示 trusted。作为实验中的管理员角色,create extension hstore(受信任)可以成功,而 create extension pg_visibility(不受信任)会以 42501 失败。

动手验证 · lab 33-managed
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');

完整实验脚本

RDS 的 rds.allowed_extensions 默认值为 '*',并且是动态设置(AWS);Azure 要求先在 azure.extensions 中“把扩展加入允许列表”(Microsoft);在 Cloud SQL 上,只有 cloudsqlsuperuser 成员可以创建扩展。AWS 说明扩展版本“不会在引擎版本升级流程中自动升级”。

现在,卷成了你的瓶颈

在托管服务上,你很少会先耗尽 CPU。你会先耗尽 I/O,因为数据库位于网络块存储上,而它有预置上限;这些数字比人们想象的更小:400 GiB 以下的 RDS gp3 卷基线是 3,000 IOPS,128 GiB 的 Azure Premium SSD 是 500 IOPS。

两个上限,你会撞上其中较低的那个

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

  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

在小实例后面接一块更快的卷不会带来任何收益,而小实例的突发额度即使在流量没有变化时也会耗尽。

I/O 被节流时,会表现为 IOPS 持平而延迟上升

在 Postgres 内部,活动会话会等待 IO,主要是 DataFileRead(某个页面不在共享缓冲区中);可以多采样几次等待事件。在服务商的指标中,它就是本章开头所说的情形:IOPS 顶在某个数字上,而延迟和队列深度持续上升。然后按本书的顺序修复:少读页面,给工作集更多内存,最后才购买 IOPS。

深入阅读:各服务商卖什么,以及哪些指标显示了上限

截至 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)。Azure Premium SSD 在 128 GiB 时提供 500 IOPS,并可“突发到最高 3,500”,512 GiB 时提供 2,300;v2 在任何容量都从 3,000 开始(Microsoft)。Cloud SQL 的 SSD IOPS 随机器类型和容量变化,并且“不能配置这些值”;Hyperdisk Balanced 在 20 到 256 GB 范围内默认提供 4,000 IOPS(Google)。Supabase 说得很清楚:“有效 IOPS 取计算规格支持的 IOPS 与磁盘预置 IOPS 中较低者”,其 Micro 计算规格的基线是 500 IOPS(Supabase)。AWS 警告某个实例“可能无法充分利用预置的 IOPS”;Microsoft 说 VM 类型“也有 IOPS 限制”;Google 说“机器类型决定上限”。

信号RDS(CloudWatch)Azure
I/O 被顶在上限ReadIOPS、WriteIOPS、ReadThroughputdisk_iops_consumed_percentage
请求排队DiskQueueDepth、ReadLatencydisk_queue_depth
I/O 突发额度正在耗尽BurstBalance(gp2)、EBSIOBalance%disk_iops_consumed_percentage 接近 100
CPU 突发额度正在耗尽T 类实例的 CPUCreditBalance可突发实例的 cpu_credits_remaining
表 34-4 I/O 被节流时,会表现为 IOPS 持平而延迟上升

名称来自 AWS 和 Microsoft;AWS 的 IO:DataFileRead 页面描述了同一种等待。EBSIOBalance% 是实例的突发额度池:gp3 卷没有突发能力,但它前面的实例可能有。

分离式存储改变了部分规则

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

深入阅读:Aurora、AlloyDB 和 Neon 如何描述自己的存储

截至 2026 年 10 月:

  • Aurora 在三个可用区之间把写入复制到“六个存储节点”;副本读取同一个卷,延迟“通常远低于 100 毫秒”(存储、复制、HA)。它的参数参考中没有 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)。
  • Neon“在达到法定人数的 safekeeper 通过 Paxos 协议确认 WAL 记录后提交”,而 pageserver 会按请求重建页面(Neon)。缩容到零的计算实例唤醒时,缓冲区是空的(相关章节给出了具体时间)。读己之写仍然需要小心:延迟很小,但不是零。

服务商仪表盘是等待事件采样器

每个服务商的性能仪表盘都是等待事件画像,只是被采样并保存下来。用同样的方式阅读它:IO 条带意味着页面正在从存储中读取,Lock 条带意味着一个锁队列。修复点仍然在查询中。

“CPU”不是等待事件

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

深入阅读:根据文档,各仪表盘采样的内容
动手验证 · lab 33-managed
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));

完整实验脚本

截至 2026 年 10 月:RDS Database Insights 每秒“采样同时运行查询的会话数量”,并将 CPU 定义为“在 CPU 上活动或正在等待 CPU”(AWS)。Cloud SQL Query Insights 只有 Enterprise Plus 才显示等待事件(Google),Google 还指出 CPU 负载“可以超过核心数上限线”。AlloyDB 将“CPU 和 CPU Wait”、“IO Wait”和“Lock Wait”分开显示(Google)。Azure Query Store 会采样等待,但在 Burstable 上不会采样,因为“会导致性能问题”(Microsoft)。仪表盘有保留期限;可观测性一章的 SQL 在任何服务上都能运行。

托管连接池要么固定会话,要么泄露状态

在事务模式中,会话状态要么会出问题,要么必须被跟踪(事务池模式会破坏会话状态),而托管连接池会选择其中一种处理方式。

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

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

深入阅读:各托管连接池的规则(按文档整理)

截至 2026 年 10 月:RDS Proxy 还会在游标、LISTEN、临时表、会话级咨询锁和超过 16 KB 的语句上固定会话;如果连接池用 DISCARD ALL 重置,则所有连接都会被固定;请观察 DatabaseConnectionsCurrentlySessionPinned(AWS)。Cloud SQL 的连接池需要 Enterprise Plus,启用它“会导致数据库重启”;事务模式“不支持”SET、LISTEN 和 PREPARE(Google)。Azure 的 PgBouncer 1.25.2 将 pgbouncer.max_prepared_statements 设置为 0,“不支持 Burstable”,并且每次故障切换都会重启(Microsoft)。Neon 在 -pooler 主机名后运行 PgBouncer(Neon);Supabase 的事务模式“不支持预处理语句”(Supabase)。

备份是快照加 WAL,而恢复会创建新服务器

每项服务都按照备份一章所描述的方式构建时间点恢复:快照加归档 WAL。按钮确实有效。但它不会提醒你保留期是固定窗口(各服务商的窗口见超级用户访问和保留规则会排除哪些服务),最后几分钟的 WAL 可能还没发送出去,或者删除实例时可能会把自动备份一并删除。

恢复时间比按钮提示的更长

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

深入阅读:各服务商的 RPO、删除行为和惰性加载

截至 2026 年 10 月:

  • RDS“每五分钟将事务日志上传到 Amazon S3”,并且除非另有指定,“所有自动备份都会随 DB 实例一起删除”(PITR、备份)。请求尚未加载的恢复数据块时,会“立即从 Amazon S3 下载请求的数据”;AWS 建议扫描整张表来预热热点表(AWS)。
  • Cloud SQL:“时间点恢复总是会创建一个新实例”(Google);对于已删除的实例,请在“四天内”联系支持团队(备份)。
  • Azure:“延迟 RPO……可能最长达到五分钟”(Microsoft)。

蓝绿升级就是逻辑升级,限制也会一并到来

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

深入阅读:原地升级和蓝绿升级的细则

原地升级:RDS 表示“直到升级完成前,你都会经历中断”;Cloud SQL;Azure要求“在每个数据库中运行 ANALYZE 命令”。RDS 蓝绿部署对支持的大版本升级使用逻辑复制(AWS),表示切换“通常不到一分钟”;根据其注意事项:蓝环境上的 DDL 会使复制进入降级状态并迫使重建(禁用会自行运行 DDL 的 pg_partman),序列只在切换时同步,大对象和不记录日志的表不会复制,而单线程应用变更时可能跟不上繁忙的蓝环境。

故障切换依赖 DNS,你的连接池必须注意到

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

深入阅读:文档中的故障切换时间,以及会拉长它们的细节
部署方式文档给出的典型故障切换时间
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 秒内且零数据丢失”
表 34-5 故障切换依赖 DNS,你的连接池必须注意到

来源,2026 年 10 月:RDS Multi-AZ、RDS 集群、Aurora、Cloud SQL、AlloyDB、Azure。AWS:“大型事务或漫长的恢复过程可能增加故障切换时间”。Microsoft:备用实例“通常以 40 MB/s 的速度恢复 WAL 文件”,并且从 PostgreSQL 17 开始,只要配置正确,向备用实例同步复制槽就由原生功能提供。RDS 和 Azure 会移动 DNS;Cloud SQL 和 AlloyDB 保留静态 IP。

不能 SSH 登录,所以要规划日志

没有 Shell 就没有 tail -f。日志存放在服务商指定的位置,并且只保留服务商规定的时长:RDS 默认保留日志文件 3 天,最多 7 天(AWS)。第一天就设置值得保留的日志,并把它发送到你能控制的地方,否则在事故复盘前证据就会消失。

账单也有自己的故障模式

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

最昂贵的陷阱,是为了慢查询而升级的实例:关系一章中的删除操作,在没有索引时会读取 events 的全部 35,173 个页面,有索引时只读三个页面。RAM 会掩盖问题;索引可以修复它,而且每月不增加任何费用。

本章要点

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

延伸阅读

表格阅读