pg_upgrade — 升级一个PostgreSQL服务器实例
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 版。
升级集簇会使目标集簇执行由源集簇超级用户所选择的任意代码。升级前, 务必确认源集簇超级用户是受信任的。
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 就会报错。目前,它在 Linux(内核 4.5 或更高版本) 上的 Btrfs 和 XFS(文件系统需在创建时启用 reflink 支持)以及 macOS 上的 APFS 中受支持。
--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 在从现有集簇升级时会保留该有符号性设置。 然而,从 PostgreSQL 17 或更早版本升级时, pg_upgrade 会采用其构建所在平台的 char 有符号性。
该选项允许你显式设置新集簇默认的 char 有符号性,以覆盖任何继承值。 该选项主要适用于以下两种场景:
如果你计划在升级后迁移到不同平台,则不应使用该选项。在这种情况下,默认行为才是正确的。 应当先在原平台上不带此选项完成升级,然后再迁移集簇。这是推荐且最安全的方法。
如果你已经将集簇迁移到了默认 char 有符号性不同的平台上 (例如从基于 x86 的系统迁移到基于 ARM 的系统),则应使用该选项来指定与原平台 默认 char 有符号性相匹配的值。此外,从迁移数据文件到运行 pg_upgrade 之间,切勿修改任何数据文件。 pg_upgrade 应当是新平台上首次启动该集簇的操作。
--swap将数据目录从旧集簇移动到新集簇,然后用为新集簇生成的系统目录文件替换原有目录文件。 该模式的性能可能优于 --link、--clone、 --copy 和 --copy-file-range,尤其是在关系很多的集簇上。
但是,这种模式会在旧集簇中产生大量垃圾文件,如果使用 --sync-method=syncfs,可能会延长文件同步步骤。因此, 建议在 --swap 模式下使用 --sync-method=fsync。
此外,一旦文件传输步骤开始,旧集簇就会被破坏性修改,因此不再能安全启动。 详见 Step 17。
--sync-method=method设置为 fsync(默认)时,pg_upgrade 会递归打开并同步 升级后集簇数据目录中的所有文件。查找文件时会跟随 WAL 目录和各个已配置表空间的符号链接。
在 Linux 上,也可以使用 syncfs,让操作系统同步包含升级后集簇数据目录、 其 WAL 文件以及各个表空间的整个文件系统。见 recovery_init_sync_method 了解使用 syncfs 时需要注意的事项。
当使用 --no-sync 时,此选项无效。
-?--help显示帮助,然后退出
使用 pg_upgrade 执行升级的步骤如下:
移动旧集簇(可选)
如果你使用的是带版本号的安装目录,例如 /opt/PostgreSQL/16,则不必移动旧集簇。 图形化安装程序都使用带版本号的安装目录。
如果你的安装目录不是带版本号的,例如 /usr/local/pgsql, 就必须移动当前的 PostgreSQL 安装目录,以免它干扰新的 PostgreSQL 安装。当前的 PostgreSQL 服务器关闭后,就可以安全地重命名 PostgreSQL 安装目录。假设旧目录是 /usr/local/pgsql, 你可以这样做:
mv /usr/local/pgsql /usr/local/pgsql.old
以上命令会重命名该目录。
对于源码安装,编译新版本
使用与旧集簇兼容的 configure 标志编译新的 PostgreSQL 源码。 pg_upgrade 会在开始升级前检查 pg_controldata, 以确保所有设置都兼容。
安装新的 PostgreSQL 二进制文件
安装新服务器的二进制文件和支持文件。默认安装中包含 pg_upgrade。
对于源码安装,如果你希望把新服务器安装到自定义位置,可以使用 prefix 变量:
make prefix=/usr/local/pgsql.new install
初始化新的 PostgreSQL 集簇
使用 initdb 初始化新集簇。这里同样要使用与旧集簇匹配的兼容 initdb 标志。许多预构建的安装程序会自动执行该步骤。 无需启动新集簇。
安装扩展共享对象文件
许多来自 contrib 或其他来源的扩展和自定义模块都会使用 共享对象文件(或 DLL),例如 pgcrypto.so。如果旧集簇使用了这些模块, 则必须在新集簇中安装与新服务器二进制相匹配的共享对象文件,这通常通过操作系统命令完成。 不要装载模式定义,例如 CREATE EXTENSION pgcrypto, 因为这些会从旧集簇复制过来。如果有可用的扩展更新, pg_upgrade 会报告这一点,并创建一个稍后可运行的脚本来更新它们。
复制自定义全文检索文件
将所有自定义全文检索文件(词典、同义词、词库、停用词)从旧集簇复制到新集簇。
调整认证
pg_upgrade 会多次连接旧服务器和新服务器,因此你可能希望将认证设置为 peer(在 pg_hba.conf 中),或者使用 ~/.pgpass 文件(见 Section 32.16)。
停止两个服务器
确保两个数据库服务器都已停止,在 Unix 上例如:
pg_ctl -D /opt/PostgreSQL/12 stop pg_ctl -D /opt/PostgreSQL/16 stop
或者在 Windows 上使用合适的服务名:
NET STOP postgresql-12 NET STOP postgresql-16
在此次关闭期间,流复制和日志传送备库必须保持运行,以便接收所有更改。
为备库升级做准备
如果你要使用Step 11节概述的方法升级备库, 请通过对旧主库集簇和备库集簇运行 pg_controldata 来确认旧备库已追上主库。 确认所有集簇中的 “Latest checkpoint location” 值一致。另外, 请确保 wal_level 没有被设置为 minimal, 这一点可在新主库集簇的 postgresql.conf 文件中检查。
运行 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/16/data"
--old-bindir "C:/Program Files/PostgreSQL/12/bin"
--new-bindir "C:/Program Files/PostgreSQL/16/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 模块,并在升级后将其安装到新集簇。
升级流复制和日志传送备库
如果你使用了链接模式,并且有流复制(见 Section 26.2.5) 或日志传送(见 Section 26.2)备库,可以按照下列步骤快速升级它们。 你无需在备库上运行 pg_upgrade,而是要在主库上运行 rsync。现在先不要启动任何服务器。
如果你没有使用链接模式、没有或不想使用 rsync, 或者想要更简单的方案,请跳过本节说明;待 pg_upgrade 完成且 新主库集簇启动后,直接重建备库即可。
在备库上安装新的 PostgreSQL 二进制文件
确保所有备库都已安装新的二进制文件和支持文件。
确保新的备库数据目录不存在
确保新的备库数据目录不存在,或者为空。如果运行过 initdb,请删除备库的新数据目录。
安装扩展共享对象文件
在新备库上安装与新主库集簇中相同的扩展共享对象文件。
停止备库
如果备库仍在运行,请按上述说明立即停止它们。
保存配置文件
保存旧备库配置目录中需要保留的配置文件,例如 postgresql.conf (以及其包含的所有文件)、postgresql.auto.conf、 pg_hba.conf,因为在下一步中它们会被覆盖或删除。
运行 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/16 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_16_202307071 standby.example.com:/vol1/pg_tblsp
如果你把 pg_wal 迁移到了数据目录之外,也必须对这些目录运行 rsync。
配置流复制和日志传送备库
为服务器配置日志传送。(由于备库仍与主库保持同步,因此无需运行 pg_backup_start() 和 pg_backup_stop(), 也无需进行文件系统备份。)如果旧主库早于 17.0,则主库上的任何槽都不会复制到新备库, 因此旧备库上的所有槽都必须手工重建。如果旧主库为 17.0 或更高版本, 则只有主库上的逻辑槽会复制到新备库,而旧备库上的其他槽不会复制, 因此也必须手工重建。
恢复 pg_hba.conf
如果你修改了 pg_hba.conf,请恢复其原始设置。 也可能需要调整新集簇中的其他配置文件以匹配旧集簇,例如 postgresql.conf(以及其包含的所有文件)和 postgresql.auto.conf。
启动新服务器
现在可以安全地启动新服务器,然后启动所有经 rsync 同步的备库。
升级后处理
如果需要任何升级后处理,pg_upgrade 在完成时会发出警告。它还会生成必须由管理员运行的 脚本文件。这些脚本会连接到每个需要升级后处理的数据库。每个脚本应通过以下方式运行:
psql --username=postgres --file=script.sql postgres
这些脚本可以按任意顺序运行,运行完毕后即可删除。
一般来说,在重建脚本运行完成之前访问其中引用的表是不安全的;这样做可能产生错误结果 或较差的性能。未在重建脚本中引用的表则可以立即访问。
统计信息
除非指定 --no-statistics,pg_upgrade 会将大多数 优化器统计信息从旧集簇传输到新集簇。但这不会传输所有统计信息,例如通过 CREATE STATISTICS 显式创建的统计信息、扩展添加的自定义统计信息, 或由累积统计系统收集的统计信息。
由于并非所有统计信息都会由 pg_upgrade 传输,升级结束时你会被要求 运行命令来重新生成这些信息。你可能需要设置连接参数以匹配新集簇。
首先,使用 vacuumdb --all --analyze-in-stages --missing-stats-only 为尚无统计信息的关系快速生成最基本的优化器统计信息。然后,使用 vacuumdb --all --analyze-only 以确保所有关系都拥有最新的累积统计信息, 用于触发 vacuum 和 analyze。对于这两个命令,使用 --jobs 都可以加快速度。 如果 vacuum_cost_delay 被设置为非零值,可以通过 PGOPTIONS 覆盖它来加快统计信息生成,例如 PGOPTIONS='-c vacuum_cost_delay=0' vacuumdb ...。
删除旧集簇
一旦你确认升级结果令人满意,就可以运行 pg_upgrade 完成时提到的脚本, 删除旧集簇的数据目录。(如果旧数据目录中包含用户定义的表空间,则无法自动删除。) 你也可以删除旧的安装目录(例如 bin、share)。
恢复到旧集簇
如果在运行 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.4 所述。 如果你的文件系统支持文件系统快照或写时复制文件副本, 也可以用这些方式备份旧集簇和表空间,不过快照和副本必须同时创建,或者在数据库服务器关闭时创建。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。