PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

pg_upgrade

pg_upgrade — 升级一个 PostgreSQL 服务器实例

Synopsis

pg_upgrade -b oldbindir [-B newbindir] -d oldconfigdir -D newconfigdir [option...]

描述

pg_upgrade(以前称为 pg_migrator)允许把存储在 PostgreSQL 数据文件中的数据升级到更新的 PostgreSQL 大版本,而无需大版本升级通常所需的数据转储/恢复操作,例如从 12.14 升级到 13.10,或从 14.9 升级到 15.5。对于小版本升级(例如从 12.7 升级到 12.8,或从 14.1 升级到 14.5)则不需要它。

PostgreSQL 的大版本发布通常会添加一些经常改变系统表布局的新功能,但内部数据存储格式很少改变。pg_upgrade 正是利用这一点,通过创建新的系统表并直接复用旧的用户数据文件来执行快速升级。如果未来的某个大版本以一种使旧数据格式变得不可读的方式改变了数据存储格式,pg_upgrade 将无法用于这类升级。(社区会尽力避免出现这种情况。)

pg_upgrade 会尽最大努力确保旧集群和新集群是二进制兼容的,例如检查兼容的编译时设置,包括 32/64 位二进制文件。任何外部模块也必须二进制兼容,这一点很重要,尽管 pg_upgrade 无法对此进行检查。

pg_upgrade 支持从 9.2.X 及更高版本升级到 PostgreSQL 的当前大版本,包括快照版本和 beta 版本。

Warning

升级集群会导致目标端执行由源端超级用户任意选择的代码。在升级之前,请确保源端超级用户是可信的。

选项

pg_upgrade 接受下列命令行参数:

-b bindir
--old-bindir=bindir

旧的 PostgreSQL 可执行文件目录;环境变量 PGBINOLD

-B bindir
--new-bindir=bindir

新的 PostgreSQL 可执行文件目录;默认为 pg_upgrade 所在的目录;环境变量 PGBINNEW

-c
--check

仅检查集群,不更改任何数据

-d configdir
--old-datadir=configdir

旧数据库集群的配置目录;环境变量 PGDATAOLD

-D configdir
--new-datadir=configdir

新数据库集群的配置目录;环境变量 PGDATANEW

-j njobs
--jobs=njobs

要使用的并发连接数和进程/线程数

-k
--link

使用硬链接而不是把文件复制到新集群

-N
--no-sync

默认情况下,pg_upgrade 会等待被升级集群的所有文件都安全地写入磁盘。这个选项会让 pg_upgrade 不等待就返回,这样速度更快,但意味着随后发生的操作系统崩溃可能会使数据目录损坏。一般来说,这个选项对测试很有用,但不应在生产安装中使用。

-o options
--old-options options

要直接传递给旧的 postgres 命令的选项;多次指定的选项会被追加

-O options
--new-options options

要直接传递给新的 postgres 命令的选项;多次指定的选项会被追加

-p port
--old-port=port

旧集群的端口号;环境变量 PGPORTOLD

-P port
--new-port=port

新集群的端口号;环境变量 PGPORTNEW

-r
--retain

即使在成功完成之后也保留 SQL 和日志文件

-s dir
--socketdir=dir

升级期间用于 postmaster 套接字的目录;默认为当前工作目录;环境变量 PGSOCKETDIR

-U username
--username=username

集群的安装用户名;环境变量 PGUSER

-v
--verbose

启用详细的内部日志记录

-V
--version

显示版本信息,然后退出

--clone

使用高效的文件克隆(在某些系统上也称为 “reflinks”)而不是把文件复制到新集群。这可以使数据文件近乎瞬时地完成复制,在获得 -k/--link 的速度优势的同时,保持旧集群原封不动。

文件克隆只在某些操作系统和文件系统上受支持。如果选择了它但不被支持,pg_upgrade 的运行会报错。目前,它在带有 Btrfs 和 XFS(在启用了 reflink 支持而创建的文件系统上)的 Linux(内核 4.5 或更高版本)上,以及带有 APFS 的 macOS 上受支持。

--copy

把文件复制到新集群。这是默认行为。(另请参阅 --link、--clone、--copy-file-range 和 --swap。)

--copy-file-range

使用 copy_file_range 系统调用进行高效复制。在某些文件系统上,这会给出与 --clone 类似的结果(共享物理磁盘块),而在其他文件系统上它可能仍会复制块,但会通过一条优化的路径来进行。目前,它在 Linux 和 FreeBSD 上受支持。

--no-statistics

不要把统计信息从旧集群恢复到新集群中。

--set-char-signedness=option

手工设置新集群的默认 char 符号性。可能的取值是 signed 和 unsigned。

在 C 语言中,char 类型的默认符号性(在未显式指定时)因平台而异。例如,在 x86 CPU 上 char 默认为 signed char,而在 ARM CPU 上默认为 unsigned char。

从 PostgreSQL 18 开始,数据库集群会维护自己的默认 char 符号性设置,该设置可用于在具有不同默认 char 符号性的平台之间确保行为一致。默认情况下,pg_upgrade 在从现有集群升级时会保留其 char 符号性设置。但是,当从 PostgreSQL 17 或更早版本升级时,pg_upgrade 会采用其构建所在平台的 char 符号性。

此选项允许你显式设置新集群的默认 char 符号性,覆盖任何继承而来的值。有两种特定的场景会用到此选项:

  • 如果你计划在升级之后迁移到另一个平台,则不应使用此选项。在这种情况下,默认行为就是正确的。此时应该不使用该选项,先在原平台上完成升级,然后再迁移集群。这是推荐且最安全的做法。

  • 如果你已经把集群迁移到了具有不同 char 符号性的平台(例如,从基于 x86 的系统迁移到基于 ARM 的系统),你应该使用这个选项来指定与原平台默认 char 符号性相匹配的符号性。此外,在迁移数据文件与运行 pg_upgrade 之间,务必不要修改任何数据文件。pg_upgrade 应该是在新平台上启动该集群的第一个操作。

--swap

把数据目录从旧集群移动到新集群,然后把系统目录文件替换为新集群所生成的那些文件。在拥有许多关系的集群上,这种模式可以胜过 --link、--clone、--copy 和 --copy-file-range。

但是,这种模式会在旧集群中产生许多垃圾文件,如果使用了 --sync-method=syncfs,这可能会延长文件同步步骤。因此,建议将 --sync-method=fsync 与 --swap 搭配使用。

此外,一旦文件传输步骤开始,旧集群就会被破坏性地修改,因此将不再能安全地启动。详见 Step 17。

--sync-method=method

当设置为 fsync(默认值)时,pg_upgrade 会递归地打开并同步被升级集群数据目录中的所有文件。对文件的搜索会跟随 WAL 目录和每个已配置表空间的符号链接。

在 Linux 上,可以改用 syncfs 来要求操作系统同步包含被升级集群数据目录、其 WAL 文件以及每个表空间的整个文件系统。有关需要注意的事项,请参见 recovery_init_sync_method 中关于使用 syncfs 时应当了解的内容。

在使用 --no-sync 时,这个选项没有效果。

-?
--help

显示帮助信息,然后退出

用法

下面是使用 pg_upgrade 执行升级的步骤:

Note

这里没有涵盖升级 逻辑复制集群 的步骤;详情请参见 Section 29.13。

  1. (可选)移动旧集群

    如果你使用的是特定于版本的安装目录,例如 /opt/PostgreSQL/18,那么就不需要移动旧集群。图形化安装程序都使用特定于版本的安装目录。

    如果你的安装目录不是特定于版本的,例如 /usr/local/pgsql,就有必要移动当前的 PostgreSQL 安装目录,以免它干扰新的 PostgreSQL 安装。当前的 PostgreSQL 服务器关闭之后,重命名 PostgreSQL 安装目录是安全的;假设旧目录是 /usr/local/pgsql,你可以这样做:

    mv /usr/local/pgsql /usr/local/pgsql.old
    

    以重命名该目录。

  2. 对于源码安装,构建新版本

    使用与旧集群兼容的 configure 标志来构建新的 PostgreSQL 源码。在开始升级之前,pg_upgrade 会检查 pg_controldata,以确保所有设置都兼容。

  3. 安装新的 PostgreSQL 可执行文件

    安装新服务器的可执行文件和支持文件。pg_upgrade 包含在默认安装中。

    对于源码安装,如果你希望把新服务器安装到一个自定义位置,可以使用 prefix 变量:

    make prefix=/usr/local/pgsql.new install
    
  4. 初始化新的 PostgreSQL 集群

    使用 initdb 初始化新集群。同样,要使用与旧集群相匹配的兼容 initdb 标志。许多预构建的安装程序会自动完成这一步骤。无需启动新集群。

  5. 安装扩展共享对象文件

    许多扩展和自定义模块(无论来自 contrib 还是其他来源)都会使用共享对象文件(或 DLL),例如 pgcrypto.so。如果旧集群使用了这些文件,则必须在新集群中安装与新服务器二进制文件相匹配的共享对象文件,通常通过操作系统命令来安装。不要加载模式定义,例如 CREATE EXTENSION pgcrypto,因为这些定义会从旧集群复制过来。如果有可用的扩展更新,pg_upgrade 会报告这一点,并创建一个可以在以后运行以更新它们的脚本。

  6. 复制自定义全文搜索文件

    把任何自定义的全文搜索文件(词典、同义词、同义词典、停用词)从旧集群复制到新集群。

  7. 调整认证

    pg_upgrade 会多次连接旧服务器和新服务器,因此你可能希望把认证设置为 peer(在 pg_hba.conf 中),或者使用 ~/.pgpass 文件(参见 Section 32.16)。

  8. 停止两台服务器

    确保两台数据库服务器都已停止,在 Unix 上例如使用:

    pg_ctl -D /opt/PostgreSQL/12 stop
    pg_ctl -D /opt/PostgreSQL/18 stop
    

    或者在 Windows 上使用正确的服务名:

    NET STOP postgresql-12
    NET STOP postgresql-18
    

    流复制和日志传送的备用服务器在这次关闭期间必须处于运行状态,以便它们接收到所有的更改。

  9. 为备用服务器升级做准备

    如果你正在使用 Step 11 一节中概述的方法升级备用服务器,请对旧的主集群和备用集群运行 pg_controldata,以验证旧的备用服务器已经跟上进度。请验证所有集群中的 “最新检查点位置” 值都一致。另外,请确保 wal_level 没有被设置为 minimal(检查新主集群上的 postgresql.conf 文件)。

  10. 运行 pg_upgrade

    始终运行新服务器的 pg_upgrade 二进制文件,而不是旧服务器的。pg_upgrade 需要指定旧集群和新集群的数据目录和可执行文件(bin)目录。你还可以指定用户和端口值,以及你希望数据文件被链接、克隆还是交换,而不是采用默认的复制行为。

    如果使用链接模式,升级会快得多(不需要复制文件),并且使用的磁盘空间更少,但在升级之后一旦启动了新集群,你将无法再访问旧集群。链接模式还要求旧集群和新集群的数据目录位于同一个文件系统中。(表空间和 pg_wal 可以位于不同的文件系统上。)克隆模式提供同样的速度和磁盘空间优势,但不会导致旧集群在新集群启动后变得不可用。克隆模式同样要求旧数据目录和新数据目录位于同一个文件系统中。这种模式只在某些操作系统和文件系统上可用。如果有很多关系,交换模式可能是最快的,但一旦文件传输步骤开始,你将无法再访问旧集群。交换模式也要求旧集群和新集群的数据目录位于同一个文件系统中。

    把 --jobs 设置为 2 或更高的值可以让 pg_upgrade 并行处理多个数据库和表空间。一个很好的起点是机器上的 CPU 核心数。对于多数据库和多表空间的服务器,这个选项可以大幅缩短升级时间。

    对于 Windows 用户,你必须登录到一个管理员账户,然后在运行 pg_upgrade 时为目录加上引号,例如:

    pg_upgrade.exe
            --old-datadir "C:/Program Files/PostgreSQL/12/data"
            --new-datadir "C:/Program Files/PostgreSQL/18/data"
            --old-bindir "C:/Program Files/PostgreSQL/12/bin"
            --new-bindir "C:/Program Files/PostgreSQL/18/bin"
    

    一旦启动,pg_upgrade 会验证两个集群是否兼容,然后执行升级。你可以使用 pg_upgrade --check 只执行检查,即使旧服务器仍在运行也没有问题。pg_upgrade --check 还会列出升级之后你需要做的任何手工调整。如果你打算使用链接、克隆、copy-file-range 或交换模式,你应该把 --link、--clone、--copy-file-range 或 --swap 选项与 --check 一起使用,以启用特定于模式的检查。pg_upgrade 要求对当前目录有写权限。

    显然,在升级期间不应该有任何人在访问集群。pg_upgrade 默认在端口 50432 上运行服务器,以避免意外的客户端连接。执行升级时,你可以对两个集群使用相同的端口号,因为旧集群和新集群不会同时运行。但是,在检查一个正在运行的旧服务器时,旧端口号和新端口号必须不同。

    如果在恢复数据库模式时发生错误,pg_upgrade 会退出,你将不得不按照下文 Step 17 中所述回退到旧集群。要再次尝试 pg_upgrade,你需要修改旧集群,使 pg_upgrade 的模式恢复能够成功。如果问题出在某个 contrib 模块上,你可能需要从旧集群中卸载该 contrib 模块,并在升级之后把它安装到新集群中(前提是该模块没有被用来存储用户数据)。

  11. 升级流复制和日志传送的备用服务器

    如果你使用了链接模式,并且拥有流复制(参见 Section 26.2.5)或日志传送(参见 Section 26.2)的备用服务器,你可以按照下面的步骤快速升级它们。你不需要在备用服务器上运行 pg_upgrade,而是在主服务器上运行 rsync。此时还不要启动任何服务器。

    如果你没有使用链接模式、没有或者不想使用 rsync,或者想要一个更简单的解决方案,请跳过本节中的说明,只需在 pg_upgrade 完成并且新的主服务器开始运行之后重新创建备用服务器即可。

    1. 在备用服务器上安装新的 PostgreSQL 可执行文件

      确保所有备用服务器上都安装了新的可执行文件和支持文件。

    2. 确保新的备用数据目录不存在

      确保新的备用数据目录不存在或者为空。如果运行过 initdb,请删除备用服务器的那些新数据目录。

    3. 安装扩展共享对象文件

      在新备用服务器上安装你安装到新主集群中的相同扩展共享对象文件。

    4. 停止备用服务器

      如果备用服务器仍在运行,现在就按照上面的说明停止它们。

    5. 保存配置文件

      从旧备用服务器的配置目录中保存任何你需要保留的配置文件,例如 postgresql.conf(以及它包含的所有文件)、postgresql.auto.conf、pg_hba.conf,因为这些文件在下一步中会被覆盖或删除。

    6. 运行 rsync

      使用链接模式时,可以用 rsync 快速升级备用服务器。为此,请从主服务器上一个位于旧、新数据库集群目录之上的目录,在 主服务器 上为每台备用服务器运行以下命令:

      rsync --archive --delete --hard-links --size-only --no-inc-recursive old_cluster new_cluster remote_dir
      

      其中 old_cluster 和 new_cluster 相对于主服务器上的当前目录,而 remote_dir 则位于备用服务器上旧、新集群目录 之上。主服务器和备用服务器上指定目录之下的目录结构必须匹配。有关指定远程目录的详细信息,请查阅 rsync 的手册页,例如:

      rsync --archive --delete --hard-links --size-only --no-inc-recursive /opt/PostgreSQL/12 \
            /opt/PostgreSQL/18 standby.example.com:/opt/PostgreSQL
      

      你可以使用 rsync 的 --dry-run 选项来验证该命令将执行的操作。虽然对于至少一台备用服务器来说 rsync 必须在主服务器上运行,但只要已升级的备用服务器尚未启动,也可以在一台已升级的备用服务器上运行 rsync 来升级其他备用服务器。

      这一步会记录 pg_upgrade 的链接模式在主服务器上创建的、用于连接旧集群和新集群中文件的那些链接。然后它会在备用服务器的旧集群中找到匹配的文件,并在备用服务器的新集群中为它们创建链接。在主服务器上没有被链接的文件会从主服务器复制到备用服务器(它们通常很小)。这提供了快速的备用服务器升级。遗憾的是,rsync 会不必要地复制与临时表和非日志表相关的文件,因为这些文件通常不存在于备用服务器上。

      如果你有表空间,你将需要为每个表空间目录运行一个类似的 rsync 命令,例如:

      rsync --archive --delete --hard-links --size-only --no-inc-recursive /vol1/pg_tblsp/PG_12_201909212 \
            /vol1/pg_tblsp/PG_18_202307071 standby.example.com:/vol1/pg_tblsp
      

      如果你把 pg_wal 移到了数据目录之外,那么也必须在那些目录上运行 rsync。

    7. 配置流复制和日志传送的备用服务器

      配置服务器进行日志传送。(你不需要运行 pg_backup_start() 和 pg_backup_stop(),也不需要做文件系统备份,因为备用服务器仍然与主服务器保持同步。)如果旧主服务器的版本低于 17.0,那么主服务器上的任何槽位都不会被复制到新备用服务器,因此旧备用服务器上的所有槽位都必须手工重新创建。如果旧主服务器的版本为 17.0 或更高,那么只有主服务器上的逻辑槽位会被复制到新备用服务器,但旧备用服务器上的其他槽位不会被复制,因此必须手工重新创建。

  12. 恢复 pg_hba.conf

    如果你修改过 pg_hba.conf,请恢复它的原始设置。可能还需要调整新集群中的其他配置文件以与旧集群相匹配,例如 postgresql.conf(以及它包含的所有文件)、postgresql.auto.conf。

  13. 启动新服务器

    现在可以安全地启动新服务器了,然后启动任何经过 rsync 同步过的备用服务器。

  14. 升级后处理

    如果需要进行任何升级后处理,pg_upgrade 会在完成时发出警告。它还会生成必须由管理员运行的脚本文件。这些脚本文件会连接到每一个需要升级后处理的数据库。每个脚本都应使用以下方式运行:

    psql --username=postgres --file=script.sql postgres
    

    这些脚本可以按任意顺序运行,并且在运行过后即可删除。

    Caution

    一般来说,在重建脚本运行完成之前,访问重建脚本所引用的表是不安全的;这样做可能产生错误的结果或糟糕的性能。未被重建脚本引用的表可以立即访问。

  15. 统计信息

    除非指定了 --no-statistics 选项,否则 pg_upgrade 会把大部分优化器统计信息从旧集群传输到新集群。这不会传输所有的统计信息,例如用 CREATE STATISTICS 显式创建的统计信息、由扩展添加的自定义统计信息,或者由累积统计系统收集的统计信息。

    由于 pg_upgrade 并不会传输所有统计信息,在升级结束时你会得到指示去运行一些命令来重新生成这些信息。你可能需要设置连接参数以匹配你的新集群。

    首先,使用 vacuumdb --all --analyze-in-stages --missing-stats-only 为没有任何统计信息的关系快速生成最小的优化器统计信息。然后,使用 vacuumdb --all --analyze-only 确保所有关系都拥有已更新的累积统计信息,以触发清理和分析。对于这两个命令,使用 --jobs 可以加快速度。如果 vacuum_cost_delay 被设置为一个非零值,可以使用 PGOPTIONS 覆盖它来加快统计信息的生成,例如 PGOPTIONS='-c vacuum_cost_delay=0' vacuumdb ...。

  16. 删除旧集群

    在对升级感到满意之后,你可以通过运行 pg_upgrade 完成时提到的脚本来删除旧集群的数据目录。(如果旧数据目录中有用户定义的表空间,则无法自动删除。)你也可以删除旧的安装目录(例如 bin、share)。

  17. 回退到旧集群

    如果在运行 pg_upgrade 之后你希望回退到旧集群,有几种选择:

    • 如果使用了 --check 选项,旧集群未被修改;可以重新启动它。

    • 如果既没有使用 --link 也没有使用 --swap,旧集群未被修改;可以重新启动它。

    • 如果使用了 --link 选项,数据文件可能在旧集群和新集群之间共享:

      • 如果 pg_upgrade 在链接开始之前中止,旧集群未被修改;可以重新启动它。

      • 如果你没有启动新集群,旧集群未被修改,只是在链接开始时,一个 .old 后缀被附加到了 $PGDATA/global/pg_control 上。要重新使用旧集群,请把 .old 后缀从 $PGDATA/global/pg_control 中去掉;然后你就可以重新启动旧集群。

      • 如果你启动了新集群,它已经写入了共享文件,此时使用旧集群是不安全的。在这种情况下,旧集群将需要从备份中恢复。

    • 如果使用了 --swap 选项,旧集群可能已被破坏性地修改:

      • 如果 pg_upgrade 在报告旧集群不再能安全启动之前中止,旧集群未被修改;可以重新启动它。

      • 如果 pg_upgrade 已报告旧集群不再能安全启动,则旧集群已被破坏性地修改。在这种情况下,旧集群将需要从备份中恢复。

环境

可以用一些环境变量来为命令行选项提供默认值:

PGBINOLD

旧的 PostgreSQL 可执行文件目录;选项 -b/--old-bindir。

PGBINNEW

新的 PostgreSQL 可执行文件目录;选项 -B/--new-bindir。

PGDATAOLD

旧数据库集群的配置目录;选项 -d/--old-datadir。

PGDATANEW

新数据库集群的配置目录;选项 -D/--new-datadir。

PGPORTOLD

旧集群的端口号;选项 -p/--old-port。

PGPORTNEW

新集群的端口号;选项 -P/--new-port。

PGSOCKETDIR

升级期间用于 postmaster 套接字的目录;选项 -s/--socketdir。

PGUSER

集群的安装用户名;选项 -U/--username。

注记

pg_upgrade 会创建各种工作文件(例如模式转储),存储在新集群目录中的 pg_upgrade_output.d 内。每次运行都会创建一个以按 ISO 8601 格式(%Y%m%dT%H%M%S)格式化的时间戳命名的新子目录,它生成的所有文件都存储在其中。如果成功完成,pg_upgrade_output.d 及其包含的文件会被 pg_upgrade 自动移除;但如果出现问题,那里的文件可能提供有用的调试信息。

pg_upgrade 会在旧数据目录和新数据目录中启动短生命周期的 postmaster。与这些 postmaster 通信所需的临时 Unix 套接字文件默认会创建在当前工作目录中。在某些情况下,当前目录的路径名可能太长而不能作为有效的套接字名称。在这种情况下,你可以使用 -s 选项把套接字文件放到某个路径名更短的目录中。为了安全起见,请确保该目录不能被任何其他用户读取或写入。(Windows 上不支持这种做法。)

如果这些情况影响到你的安装,pg_upgrade 会报告所有失败、重建和重建索引的情况;用于重建表和索引的升级后脚本会被自动生成。如果你正在尝试让许多集群的升级自动化,你应该会发现,具有相同数据库模式的集群在所有集群升级中都需要相同的升级后步骤;这是因为升级后步骤基于的是数据库模式,而不是用户数据。

对于部署测试,可以创建旧集群的一个仅包含模式的副本,插入虚拟数据,然后升级该副本。

pg_upgrade 不支持升级表列使用了这些 reg* 引用 OID 的系统数据类型的数据库:

regcollation
regconfig
regdictionary
regnamespace
regoper
regoperator
regproc
regprocedure

(regclass、regrole 和 regtype 可以升级。)

如果你想使用链接模式,但不希望在新集群启动时旧集群被修改,可以考虑使用克隆模式。如果克隆模式不可用,可以复制一份旧集群,然后用链接模式升级该副本。要制作旧集群的一个有效副本,可以在服务器运行时使用 rsync 创建旧集群的一个脏副本,然后关闭旧服务器并再次运行 rsync --checksum,用任何更改更新该副本使其达到一致。(之所以需要 --checksum,是因为 rsync 的文件修改时间粒度只有一秒。)你可能希望排除一些文件,例如 postmaster.pid,如 Section 25.3.6 中所述。如果你的文件系统支持文件系统快照或写时复制的文件副本,你可以用它来备份旧集群和表空间,不过快照和副本必须同时创建,或者必须在数据库服务器停机时创建。

另请参阅

initdb, pg_ctl, pg_dump, postgres