Postgres是一个令人惊叹的数据库系统,但它确实有一个五年的生命周期。这意味着您需要至少每五年执行一次对它的主要升级。幸运的是,Postgres附带了pg_upgrade程序,它可以快速轻松地将一个主要版本的Postgres迁移到另一个主要版本。
让我们通过一个示例来了解如何进行升级。在这种情况下,我们将从Postgres 12升级到Postgres 16。您应该始终力争升级到可能的最高版本。请查看postgresql.org以查看当前版本是什么。如果遇到困难,官方文档有很多详细信息。
pg_upgrade程序的工作原理是基于现有Postgres集簇中的数据填充一个空的Postgres集簇(新版本)。它复制系统目录信息,然后复制或硬链接数据文件。即使在一个大型的多TB系统上,如果您使用--link选项,这个过程也可能不到一分钟!让我们看看运行pg_upgrade的基本步骤。
安装
在开始之前,在开发环境中针对新版本测试您的应用程序。阅读发布说明 - 您需要检查您要跨越的每个主要版本的“点零”版本的发布说明。在我们从12升级到16的示例中,我们将希望检查Postgres
13.0、14.0、15.0、16.0以及16.x系列中最新版本的发布说明。这实际上可能是整个过程中最耗时的步骤!下一步是安装新版本。我们不仅需要新的可执行文件和库,还必须创建一个新的集簇,这将成为我们的新实例。安装取决于您的操作系统,但一般的想法是使用已有的相同软件包,但版本更高。以下是一个方法,可以帮助您在支持yum的系统上执行此操作:# yum list installed 'postgresql12*' | sed -e '1d; s/postgresql12/yum install postgresql16/; s/\..*//'
yum install postgresql16
yum install postgresql16-contrib
yum install postgresql16-libs
yum install postgresql16-server
当新的软件包版本就位后,我们需要创建一个新的集簇。为此,我们将使用initdb命令。在某些操作系统上,它甚至可能不在您的路径中。在受Red Hat启发的系统上,请检查/usr/pgsql/16/bin/initdb。一旦找到,请使用--version参数运行(作为postgres用户),以确保它是您要升级的目标版本(在我们的示例中为16)。然后使用您的真实参数运行它,以创建一个新的数据目录。务必使用用于创建旧版本Postgres数据目录的相同参数。特别要确保在这个新集簇中启用页面级校验和,如果旧集簇也启用了的话。因为各种Postgres可执行文件的更新版本可以处理旧版本的Postgres,所以我们可以将这个位置添加到我们的路径中:export PATH=$PATH:/usr/pgsql-16/bin/
initdb --version
initdb --data-checksums -D var/lib/pgsql/16
在这里不需要启动新集簇,pg_upgrade将在其过程中自行完成此操作。如果您有任何自定义扩展或者您的打包系统没有处理的东西,请根据需要现在安装它们。目前不用担心诸如postgresql.conf之类的配置文件,这些稍后再处理。验证
下一步是使用--check参数运行pg_upgrade可执行文件。这将确保一切看起来都正常,但实际上并不执行升级。pg_upgrade程序始终需要知道四个位置的信息:旧的可执行文件、新的可执行文件、旧的数据目录和新的数据目录。虽然您可以将它们作为命令行参数输入,但使用环境变量更清晰。现在让我们设置它们并运行检查:export PGBINOLD=/usr/pgsql-12/bin
export PGBINNEW=/usr/pgsql-16/bin
export PGDATAOLD=/var/lib/pgsql/12
export PGDATANEW=/var/lib/pgsql/16
pg_upgrade --check
它将提供两个Postgres集簇之间发现的任何不兼容性的详细报告。我们希望在底部看到这条消息:*Clusters are compatible*
如果您没有看到该消息,则需要解决问题。失败的常见原因是校验和,会出现以下消息:old cluster does not use data checksums but the new one does
如果发生这种情况,请删除您的新数据目录,并重新运行initdb命令,这次不使用--data-checksums参数。您随时可以稍后启用校验和。备份
现在是进行完整备份的绝佳时机。如果发生意外情况,我们将有一个返回旧版本的方法。启动完整备份:pgbackrest backup --stanza=mystanza --type=full
没有pgBackRest?使用其他可以完成、安全、高效备份的系统也是可以的。如果您没有备份系统,请停止您的升级,并首先让备份系统正常工作。这比进行主要升级更为重要!准备复制节点
如果您正在使用流复制,那么这些副本也需要升级。坏消息是我们不能在副本服务器上运行pg_upgrade。好消息是升级它们的过程有些棘手 但很快。它们的升级在主服务器之后进行,但现在是准备工作的绝佳时机。对于每个副本,您需要安装新的Postgres软件,就像我们上面所做的那样,但不要运行initdb。将每个副本上数据目录内,存在的所有配置文件复制到目录之外的安全位置。升级
一旦备份和pg_upgrade --check完成,我们就准备好进行实际升级了。确保使用--link参数对pg_upgrade进行升级非常重要。这样做可以避免将所有实际数据文件从旧集簇复制到新集簇。相反,它会在旧数据文件和新数据文件之间创建硬链接(而不是更常见的符号链接)。这样做告诉操作系统,每个数据文件都有两条完全有效的路径可以到达该文件。此外,与复制文件内容相比,这种方式非常快速。命令如下所示:## This assumes the ENVs from above were exported
pg_upgrade --link
运行此命令后,它将启动新集簇,回到旧版本的方法不太容易。这就是上面的备份步骤。好消息是pg_upgrade程序经过了充分的测试,因此您不太可能需要恢复备份。pg_upgrade命令的输出将类似于以下内容:Upgrade Complete
----------------
Optimizer statistics are not transferred by pg_upgrade.
Once you start the new server, consider running:
home/greg/pg/16/bin/vacuumdb --all --analyze-in-stages
Running this script will delete the old cluster's data files:
./delete_old_cluster.sh
请注意,从Postgres 14开始,pg_upgrade将不再创建一个帮助您重新构建统计信息的shell脚本,而是建议通过vacuumdb程序进行,例如vacuumdb --all --analyze-in-stages。这将对每个数据库运行三次。第一次运行的统计目标为1(非常快速,但非常粗糙的统计信息),然后是目标为10的运行(稍慢,但统计信息更好),最后一次运行的值为default_statistics_target(除非您已更改,默认值为100)。复制并调整配置文件
现在新集簇已经准备好,连接应用程序到它之前还有一些步骤。新集簇将具有默认的postgresql.conf、pg_hba.conf和pg_ident.conf文件,这并不是您想要的。从旧集簇复制pg_hba.conf和pg_ident.conf文件过来。它们很可能可以直接使用,因为这些文件的格式很少更改。接下来,您需要将旧的postgresql.conf文件与新的文件合并。如果您习惯在postgresql.conf文件的底部进行更改,您可以轻松地复制并粘贴它们。无论哪种方式,您都应该逐步检查,确保所有设置仍然适用于新集簇。不要忘记postgresql.auto.conf或任何被"include*"选项引用的conf文件。打开校验和
如果您的新集簇没有启用页面级校验和,那么现在是启用它们的绝佳时机。Postgres提供了一个程序来将您的集簇更改为使用页面级校验和,称为pg_checksums。它将与我们上面使用的initdb位于相同的目录中,使用方法如下:pg_checksums -D $PGDATANEW --enable --verbose
该程序需要重写集簇中的每个文件,因此可能需要一些时间,但是等待是值得的。这种重写也会使下一步(同步副本)需要更长的时间;您可能想要从头开始重新创建副本。同步你的复制节点
对于这一部分,我将引导您参考官方文档中的第11步(“升级流复制和日志传送备用服务器”)。请按照那个rsync的步骤进行操作。未来可能会有更好的方法存在,所以我宁愿在这里留下一个链接,而不是复制那个庞大 的rsync命令并在此处打印它。 rsync过程很快,因为它只复制已更改的文件。它会比较主服务器上的新旧目录,然后再与副本进行比较。与之前一样,此过程的速度取决于文件数量。您可以同时升级您的副本。启动并生成统计信息
现在副本已经准备好,您可以启动主服务器。如果您没有使用pg_ctl而是使用类似systemd的东西,您可能需要调整一些配置文件或脚本,以指向新的数据目录。如果集簇启动没有错误,恭喜!您几乎拥有一个工作的集簇了。但是,Postgres在没有对表进行良好统计的情况下无法生成良好的查询计划。截至撰写本文时,Postgres不会将数据库统计信息从旧集簇复制到新集簇。这种情况将在某个时候发生改变,但在此之前,您需要自己生成统计信息。如上面pg_upgrade输出所提到的,您可以分阶段进行此操作,以尽快生成统计信息:vacuumdb --all --analyze-in-stages
vacuumdb --all --analyze-only
Postgres扩展具有版本,并且可能需要它们自己的升级。执行此操作的方法是登录到新数据库并发出:ALTER EXTENSION foobar UPDATE;
如果pg_upgrade能够检测到扩展,您将会看到如下消息:Your installation contains extensions that should be updated
with the ALTER EXTENSION command. The file
update_extensions.sql
when executed by psql by the database superuser will update
these extensions.
该文件应该包含命令,但您也可以通过运行以下命令生成可能需要升级的扩展列表:SELECT * FROM pg_available_extensions WHERE default_version <> installed_version;
一个成功的升级将不返回任何消息,而一个不需要的升级将会报错:alter extension hstore update;
ALTER EXTENSION
alter extension hstore update;
NOTICE: version "1.8" of extension "hstore" is already installed
ALTER EXTENSION
升级监控和工具
不要忘记更新与您的数据库交互的所有内容,并告知它您正在使用新的主要版本。您可能还有一个新的数据目录,某些工具需要知道这一点。请记住,主要升级意味着一个新的集簇,具有新的系统标识符,以及新的统计信息。运行另一个备份
现在是再次运行备份的好时机,这次备份的对象是新版本的数据库。pgbackrest backup --stanza=mystanza --type=full
删除旧集簇
在一切运行良好一段时间后,您可以移除旧的集簇。在其运行结束时,pg_upgrade会创建一个小型的shell脚本来帮助执行此操作。这个脚本非常简单,只执行了"rm -fr
$OLDDATADIR"。最好运行这个脚本,无论它看起来多么简单,也比冒险手指滑动并删除错误的数据目录要好!pg_upgrade需要执行多久?
好的,我听到您在问,我们知道如何去做,但需要多长时间?答案是,如果使用推荐的 --link 模式,则取决于数据目录中文件的数量。这又取决于表有多大(较大的表意味着多个文件,因为Postgres会将表在磁盘上分成1GB文件),每个主表是否需要TOAST表,以及有多少索引。但是作为一个粗略的估计,表的数量就足够了。我对我的Postgres 12升级到Postgres 16进行了一些测试,以下是结果:
使用一些简单的数学运算,我们发现,对于每增加一万个表,升级过程就需要多一分钟。您的经验可能会因操作系统和硬件不同而有所不同,但测试并获得一个大致的数字是非常简单的。以下是我使用的测试数据:## On the old version:psql -c 'do $$ begin for x in 1..10000 loop execute format($i$create table a%s()$i$, x); end loop; end; $$;'psql -c 'do $$ begin for x in 1..10000 loop execute format($i$create table ab%s()$i$, x); end loop; end; $$;'## Then time the pg_upgrade
在一个循环中超过 10,000 会导致问题,即使max_locks_per_transaction被调整到最大值,所以根据需要复制这行。您可以测试带有10,000个表的升级,然后带有20,000个表的升级,以生成每万个表额外花费的大致时间指南 - 并从中推断出其他情况。您可以像这样获取总关系数:SELECT count(*) FROM pg_class;
根据需要为所有数据库进行总结。您还可以通过在当前版本的数据目录中的基本目录下计算所有文件来从命令行检查文件数:find $OLDDATADIR/base -type f | wc -l
准备你的下一次升级
最后一项任务 - 为下一次升级做准备!Postgres每年发布一个新的主要版本。因此,每年或每两年升级一次主要版本是一个很好的解决方案。大约每三个月发布一个新的次要版本。您希望立即安装这些,因为它们只包含关键的错误修复,并且比上面的过程要容易得多!以下是关于Postgres版本14到17发布时间的简要回顾: