最近把测试环境升级到了PostgreSQL 14的开发版本,想提前体验一下新特性。结果踩了不少坑,有几个问题让我熬了好几个夜才解决。本文记录了这些踩坑的经历,包括连接数暴增、查询性能退化、复制延迟、扩展兼容性、参数变化等问题,以及每个问题的排查过程和解决方案。如果你也在考虑升级PostgreSQL 14,希望这篇文章能帮你提前避坑。

一、背景介绍

先说说为什么要升级PostgreSQL 14。

我们的生产环境用的是PostgreSQL 12,已经跑了一年多了,整体比较稳定。但是PostgreSQL 13发布之后,有几个新特性我们很感兴趣,比如并行真空、增量排序等。后来听说PostgreSQL 14的开发版本已经可用了,而且有更多令人期待的特性,比如多范围类型、存储过程改进、性能提升等。

于是我决定先在测试环境升级到PostgreSQL 14的开发版本,试试水。本来以为升级过程会很顺利,毕竟PostgreSQL的大版本升级一直都比较平滑。结果没想到,踩了好几个坑,有几个问题还挺严重的,熬了好几个夜才解决。

本文就把这些踩坑的经历记录下来,每个问题都包括现象、排查过程、根本原因和解决方案。希望能帮到同样在升级PostgreSQL 14的同学。

需要说明的是,我用的是开发版本,有些问题可能在正式版中已经修复了。但是升级过程中的排查思路和方法论,应该还是有参考价值的。

二、坑一:升级后连接数暴增

第一个坑是升级之后连接数暴增。

现象

升级之后第二天,监控告警说数据库连接数接近上限了。我们的max_connections设置的是200,平时正常使用大概在50到80之间。升级之后突然涨到了180多,差点把数据库打挂。

排查过程

首先我查了一下pgstatactivity,看看都是哪些连接。发现大部分连接都是来自应用服务器的,而且很多连接的状态是idle,也就是空闲连接。

然后我查了应用服务器的连接池配置,发现连接池的最大连接数设置的是每个实例20个,我们有10个应用实例,总共200个。这个配置在PostgreSQL 12的时候是没问题的,因为连接池会复用连接,不会真的创建200个连接。

但是升级到14之后,为什么连接数突然涨上去了呢?

我仔细对比了一下,发现升级之后应用的响应时间变慢了。响应时间变慢意味着每个连接的持有时间变长了,连接池里的连接来不及释放,所以需要创建更多的连接。

那为什么响应时间变慢了呢?我查了慢查询日志,发现有几个查询的执行时间比以前长了很多。

根本原因

经过仔细排查,发现是因为PostgreSQL 14改变了某些查询的执行计划。具体来说,是因为优化器的估算发生了变化,导致几个复杂查询选择了更差的执行计划。

其中一个查询是一个多表关联的报表查询,在PG12中用的是Hash Join,在PG14中变成了Nested Loop,性能差了几十倍。

解决方案

临时解决方案是给这几个查询加了Hint,强制使用Hash Join。然后收集了相关表的统计信息,更新之后优化器重新选择了正确的执行计划。

长期来看,需要在升级之后对所有重要查询做性能测试,确保执行计划没有退化。

三、坑二:VACUUM变慢导致表膨胀

第二个坑是VACUUM变慢导致表膨胀。

现象

升级之后一周左右,发现有几张大表的占用空间快速增长。其中一张表本来是50GB,一周之内涨到了80GB。而且查询性能也在下降,因为表变大了,扫描的数据量更多了。

排查过程

首先查了一下这几张表的统计信息,发现死元组的比例很高,说明VACUUM没有及时清理死元组。

然后查了VACUUM的执行情况,发现自动真空的执行频率比以前低了,而且每次执行的时间变长了。

我手动执行了一次VACUUM,发现确实比PG12慢了很多。一张50GB的表,在PG12中大概需要20分钟,在PG14中需要一个多小时。

根本原因

查了Release Notes和邮件列表,发现PostgreSQL 14对VACUUM做了一些改动,包括并行真空的改进。但是在某些情况下,新的VACUUM策略反而会变慢,尤其是对于有很多死元组的大表。

具体来说,是因为PG14在VACUUM的时候会做更多的索引清理工作,而且并行度的计算方式发生了变化,导致在某些配置下并行度不够。

解决方案

临时解决方案是手动调整了vacuumcostlimit和vacuumcostdelay参数,让VACUUM跑得更快一些。同时增加了maintenanceworkmem,让VACUUM可以一次处理更多的数据。

另外,对于特别大的表,改用了pg_repack或者VACUUM FULL来回收空间,比普通的VACUUM更高效。

长期来看,需要监控VACUUM的执行情况,及时调整参数。

四、坑三:流复制延迟增大

第三个坑是流复制延迟增大。

现象

我们有一主一从的流复制架构,升级之后发现从库的延迟明显增大了。以前延迟一般在几毫秒到几十毫秒,升级之后经常出现几秒甚至几十秒的延迟。

排查过程

首先查了从库的接收和回放进程,发现接收是正常的,但是回放速度跟不上。也就是说,WAL日志已经传到从库了,但是从库回放得慢。

然后查了从库的系统资源,发现CPU和IO都不算高,但是回放就是慢。

我对比了一下WAL的生成速度,发现主库的WAL生成速度比以前快了一些。可能是因为PG14的某些操作产生了更多的WAL。

根本原因

经过排查,发现是因为PG14对WAL的格式做了一些调整,从库回放的时候需要做更多的处理。而且我们的从库配置比较低,CPU性能不够,导致回放速度跟不上。

另外,我们用了同步复制,主库需要等从库确认之后才能提交,从库延迟增大之后,主库的写入性能也受到了影响。

解决方案

临时解决方案是把同步复制改成了异步复制,先保证主库的写入性能。然后给从库升级了配置,增加了CPU和内存。

另外,调整了从库的walbuffers和maxwal_size参数,让从库可以更好地处理WAL。

长期来看,需要监控复制延迟,确保从库的配置跟得上主库。

五、坑四:扩展不兼容

第四个坑是扩展不兼容。

现象

升级之后,有几个扩展用不了了。其中最严重的是pgstatstatements,这个扩展是我们用来监控慢查询的,非常重要。升级之后,pgstatstatements的视图查不到数据了。

另外还有几个扩展,比如pgbuffercache、pgfreespacemap,也都出现了不同程度的问题。

排查过程

首先查了一下扩展的版本,发现这些扩展都是针对PG12编译的,和PG14不兼容。

然后我尝试重新编译这些扩展,发现有些扩展的API发生了变化,编译报错。比如pgstatstatements在PG14中增加了一些新的字段,需要修改代码才能编译通过。

根本原因

PostgreSQL的大版本升级经常会改变扩展的API,尤其是开发版本,API变化更大。很多第三方扩展还没有来得及适配PG14,所以会出现不兼容的问题。

解决方案

对于官方自带的扩展(比如pgstatstatements),用PG14自带的版本重新安装就好了。我们之前用的是旧版本的扩展,升级数据库的时候没有同步升级扩展。

对于第三方扩展,需要等作者适配PG14,或者自己修改代码适配。在适配完成之前,只能先不用这些扩展,或者留在旧版本的PostgreSQL上。

建议在升级之前,先确认所有用到的扩展都有支持新版本的版本。

六、坑五:参数变化导致行为改变

第五个坑是参数变化导致行为改变。

现象

升级之后,有一些应用的行为发生了微妙的变化,但是又说不上来哪里不对。比如有些查询的结果排序和以前不一样了,有些事务的隔离级别表现不同了。

排查过程

我仔细对比了PG12和PG14的参数,发现有几个参数的默认值发生了变化。

其中一个是idleintransactionsessiontimeout,在PG12中默认是0(不超时),在PG14中默认变成了1小时。我们有一些长事务,升级之后被自动终止了,导致应用报错。

另一个是password_encryption,默认从md5变成了scram-sha-256。我们有一些老的客户端不支持scram认证,升级之后连不上数据库了。

还有一些优化器相关的参数默认值也变了,导致执行计划的选择发生了变化。

根本原因

每个PostgreSQL大版本都会调整一些参数的默认值,这是正常的。但是如果没有注意到这些变化,就可能导致应用行为改变。

解决方案

升级之后,仔细检查所有参数的默认值变化,对于影响应用行为的参数,改回原来的值,或者调整应用来适应新的默认值。

我们的做法是,把所有重要参数都显式设置在postgresql.conf中,不依赖默认值。这样升级的时候就不会因为默认值变化而出问题了。

七、坑六:逻辑复制问题

第六个坑是逻辑复制的问题。

现象

我们有一个逻辑复制的订阅,从主库复制几张表到另一个库做数据分析。升级之后,逻辑复制断了,而且重新初始化也失败。

排查过程

查了日志,发现逻辑复制的进程报错,说WAL格式不兼容。因为PG14的逻辑复制协议发生了一些变化,旧版本的订阅者无法解析新版本的WAL。

根本原因

PostgreSQL的逻辑复制协议在大版本之间可能不兼容。如果发布者和订阅者的版本不一致,就可能出现问题。

解决方案

把订阅者也升级到了PG14,逻辑复制就恢复正常了。

建议在使用逻辑复制的时候,发布者和订阅者尽量保持大版本一致,或者确认两个版本之间的逻辑复制是兼容的。

八、升级建议

踩了这么多坑,总结一些升级建议。

1. 不要在生产环境直接升级开发版本

开发版本不稳定,可能有各种bug。如果想体验新版本,先在测试环境充分测试,等正式版发布之后再升级生产环境。

2. 升级前做好备份

升级之前一定要做全量备份,最好是文件系统级别的备份。这样升级出问题的时候可以快速回滚。

3. 充分测试

升级之后要做充分的测试,包括功能测试、性能测试、压力测试。重点测试常用的查询和事务,确保执行计划没有退化,性能没有下降。

4. 检查扩展兼容性

确认所有用到的扩展都有支持新版本的版本。对于不兼容的扩展,提前找到替代方案或者自己适配。

5. 检查参数变化

仔细阅读新版本的Release Notes,了解参数默认值的变化和行为的改变。对于重要的参数,显式设置,不依赖默认值。

6. 监控升级后的表现

升级之后密切监控数据库的各项指标,包括连接数、查询性能、复制延迟、表大小等。发现异常及时处理,不要等问题积累大了再解决。

7. 有回滚方案

升级之前制定好回滚方案,如果升级出问题,能快速回滚到旧版本。减少故障时间。

九、写在最后

PostgreSQL 14有很多令人期待的新特性,升级是值得的。但是大版本升级从来都不是一件简单的事情,需要充分的准备和测试。

我这次踩的这些坑,有一些是开发版本的bug,有一些是我自己准备不足导致的。希望通过这篇文章,能让大家在升级的时候少走一些弯路。

如果你也在升级PostgreSQL,或者准备升级,有什么问题或者经验,欢迎在评论区交流。

最后用一句话结束本文:"升级有风险,操作需谨慎。"做好充分的准备,才能平稳地完成升级,享受新版本带来的好处。