资讯动态

PostgreSQL 13/14/15版本差异与升级避坑指南

发布时间:2026/10/14 4:32:35 来源:尧图企业网站定制
做数据库选型和升级规划PostgreSQL 13、14、15这三个版本到底改了什么是很多团队绕不开的问题。三个大版本虽然发布时间只隔两年但差异一点都不小13像是在12的底子上做局部性能补强14把连接能力和索引维护的瓶颈一次性打通15则引入了MERGE、逻辑复制行列过滤等能力同时把安全默认值收紧了。如果你正准备从老版本升级或者要在新项目里定版这篇文章会把三者的差异拆开讲透并且给出升级路径和避坑经验。这里不是给你贴官方release note而是站在实际使用的角度把每个改动背后的瓶颈、收益和使用场景讲清楚。适合DBA、后端开发、架构师以及所有正在纠结要不要升级、升级到哪个版本的人。1. 三个版本先看整体差异和选型思路1.1 版本时间线与定位PostgreSQL 13于2020年9月发布14于2021年9月发布15于2022年10月发布节奏上保持了一年一个大版本。用一句话概括三个版本各自的性格13是优化型把12时代遗留的索引膨胀、vacuum低效、UUID依赖扩展等问题集中处理了一遍14是性能型重点解决大连接数场景的内存开销、索引WAL放大、子事务性能这些偏底层的硬骨头15是能力型把SQL标准特性、逻辑复制的灵活度、安全默认值拉到了一个新的高度。1.2 一张表看清版本之间的核心差异功能方向PostgreSQL 13PostgreSQL 14PostgreSQL 15索引空间B-tree索引去重B-tree WAL写入量下降唯一索引支持NULLS NOT DISTINCT查询性能增量排序聚合与PL/pgSQL提速内存排序最高提升约2倍并发能力并行vacuum大连接数内存优化并行vacuum进一步成熟SQL能力gen_random_uuid()内置JSONB下标、multirangeMERGE、标准正则函数逻辑复制分区表发布/订阅基础能力增强行过滤、列列表安全与默认值————public schema权限收紧、wal_compression默认开启从这个表能看出版本之间不是单纯堆功能而是有明显侧重。如果还在12或更早跳到14或15的收益会非常大如果已经在13至少升到14才能拿到并发方面的核心收益新项目我通常建议直接选15或更新的版本省得后期再折腾一次升级。2. PostgreSQL 13稳定底座上的性能补强2.1 B-tree索引去重让索引“减脂”B-tree索引去重是13里含金量最高的一项改动。在12及更早版本中每次插入重复键值时B-tree的叶子节点都会为每个键值单独存储一条索引项键值本身要重复写一遍。假如某张表的某列只有几个枚举值索引里就会堆满完全相同的键值空间浪费严重索引膨胀扫描和写入都变慢。13引入的去重机制会把同一个键值合并成一个条目后面挂一个tid列表。这样重复键值只需要存储一次索引体积大幅下降插入时的WAL写入量也随之减少。官方发布说明和不少社区测试都提到重复度高的列上索引体积减少一半以上是很常见的。要注意的是deduplicate_items参数默认开启对于唯一性很高的索引没有明显收益但也不会带来什么副作用一般不需要手动去动它。2.2 增量排序让小范围排序省下大内存增量排序解决的是一个很隐蔽的浪费查询计划在拿到已经部分有序的输入后还会按照老办法sort整个结果集。比如已经通过索引扫描让结果按a字段有序再需要ORDER BY a, b时老逻辑依然会从头到尾排一遍。13的Incremental Sort能识别这种预排序状态把结果集切分成多个已经有序的批次只对剩下的键做排序然后逐个批次合并输出。它的内存占用比全量排序小响应时间也更短。在EXPLAIN里看到Incremental Sort节点时说明优化器已经认定它能降低成本。对ORDER BY ... LIMIT n这类查询效果尤其明显因为排序过程可以在提前拿到足够行数时直接截断不必处理完整结果集。2.3 并行vacuum和崩溃恢复加速vacuum对PostgreSQL来说几乎等同于日常保洁。13之前大表的vacuum只能由一个后台进程扫全表更新频繁的大表经常出现清理不过来、表膨胀越来越严重的情况。13开始支持VACUUM (PARALLEL n)也可以配置autovacuum参数让多个worker并行扫描堆表上的dead tuplevacuum耗时明显缩短。对7x24跑着的业务这是实打实的维护收益。另一个偏底层的优化是崩溃恢复阶段的WAL重放。13改进了重放过程中的行查找逻辑恢复时间更短。虽然平时感受不到但一旦真出现宕机少几十分钟恢复时间就是救命级别的差异。2.4 内置UUID和分区表逻辑复制开发人员最直接的感受是gen_random_uuid()终于内置了。以前要生成UUID得先CREATE EXTENSION pgcrypto否则就只能另找方案。13之后直接SELECT gen_random_uuid()就能用少一个依赖部署简化很多。逻辑复制方面13允许直接把分区表当作发布/订阅对象。之前只能逐个发布子分区运维非常繁琐。现在建了分区表发布订阅可以以根分区为单位增删子分区自动同步。对分库分表改造、多区域数据汇聚这类场景非常实用。3. PostgreSQL 14并发、索引与开发体验的全面升级3.1 大连接场景的底层重构14最值得关注的是连接数扩展。很多团队在连接数超过两三千时会明显看到内存飙升、上下文切换加剧甚至触发OOM。14通过动态共享内存和更紧凑的数据结构显著降低了每个后端进程的固定内存开销。社区里的实测反馈是相同配置下能支撑的连接数明显上了一个台阶尤其是空闲连接的消耗下降非常明显。这里要说明一点连接数的优化不等于可以无限加连接。连接池依然是标准做法14解决的是资源上限问题让max_connections从“不敢调大”变成“可以调大”但必须结合shared_buffers、work_mem这些参数统筹考虑别把内存全吃光。升级到14之后我习惯先调低单个连接的预估内存再逐步压测连接数上限而不是直接把max_connections翻倍。3.2 B-tree写放大削减与vacuum增强14在索引维护方面的升级同样重要。B-tree索引在做删除和更新时会生成大量WAL日志14把这一块的写入量大幅降了下来。官方发布说明里明确提到了这个变化实际效果是高频更新表的WAL归档和从库网络传输压力都小了不少磁盘寿命也能省一点。vacuum方面13的并行vacuum还只针对堆表扫描14把并行能力延伸到了索引清理阶段。大表的vacuum整体更快这也间接缓解了表膨胀问题。如果你管理的库有大量频繁update的表从13升到14之后autovacuum的工作压力会明显减轻。3.3 子事务与PL/pgSQL性能嵌套事务SAVEPOINT用得多的话14会带来一个不小的性能惊喜。之前的版本里子事务记录是线性扫描一次大事务里来回开几千个子事务CPU开销很夸张。14把子事务查找改成哈希查找避免了之前最主要的性能瓶颈。批量任务里大量使用savepoint做部分回滚的升级后体感很明显。PL/pgSQL函数的执行性能也有提升。服务端大量使用存储过程或复杂表达式函数时能感知到脚本运行时间变短。对存量系统来说这不需要改代码升级后白拿的性能优化是最舒服的。3.4 JSONB下标、multirange与pipeline模式开发体验层面14引入了JSONB下标赋值语法。以前更新JSONB字段要写jsonb_setSQL很啰嗦UPDATE users SET profile jsonb_set(profile, {nickname}, foo);14起可以直接写成UPDATE users SET profile[nickname] foo;JSONB用得多的系统代码可读性提升非常明显。multirange类型适合表达一段不连续的区间集合比如排班表的多个空闲时段、价格区间组合。14开始支持multirange类型以及相关的范围操作符这类数据建模变得更自然。libpq管道模式pipeline mode是给底层客户端用的允许连续发送多个查询不等前一个响应就发下一个。对短查询密集、网络往返开销大的场景比如批量取数、循环写入能有效减少客户端和服务端的交互次数。4. PostgreSQL 15SQL能力与安全默认的拐点4.1 MERGE一条SQL搞定增量同步15里最亮眼的功能是MERGE它让PostgreSQL在SQL标准的一致性上往前走了一大步。过去的UPSERT只能写INSERT ... ON CONFLICT做简单的新增或更新还行但遇到“匹配了更新匹配不上则插入或者反过来删除”这类复杂逻辑就没法一条SQL完成。一个典型例子是数据同步MERGE INTO target t USING source s ON t.id s.id WHEN MATCHED AND s.active false THEN DELETE WHEN MATCHED THEN UPDATE SET t.value s.value WHEN NOT MATCHED THEN INSERT (id, value) VALUES (s.id, s.value);这对数据同步、ETL、类CDC任务都是好消息一个语句就能完成增量处理。但要注意MERGE和ON CONFLICT不是替代关系。简单UPSERT场景下ON CONFLICT依然更轻量、更高效MERGE适合条件分支多的场景别一上来就把所有写法都换成MERGE。4.2 public schema权限收紧升级最容易踩的迁移坑15把public schema的默认权限改掉了。在此之前新建数据库后PUBLIC角色默认对public schema有CREATE和USAGE权限任何人都能在public schema里建表。15开始public schema的CREATE和USAGE权限默认只授给数据库owner。如果是从13或14用pg_upgrade升上来的旧库的授权还在通常不会立即出问题。但如果是新建的库或者用新版本做的逻辑导入应用账号就会遇到permission denied for schema public。在实际项目里这往往表现为升级后应用突然建不了表、加不了字段排查半天才发现权限问题。解决办法是显式授权GRANT CREATE, USAGE ON SCHEMA public TO myapp;多人共用一个库的项目升级前一定要梳理清楚账号权限别等应用报错再救火。4.3 逻辑复制增加行过滤与列列表15的逻辑复制有两个非常实用的功能行过滤和列列表。以前逻辑复制只有整表复制一个选项想只同步某些行或者某些列只能靠订阅端触发器或另建视图复杂度很高。15起可以在发布端直接定义规则CREATE PUBLICATION pub_orders FOR TABLE orders (id, status, create_time) WHERE (status IN (paid, shipped));这样订阅端只收到符合条件并且需要的字段数据量、网络传输和订阅端存储都会省很多。15还支持在发布端对分区表设置行过滤对分库分表场景更友好。要注意的是行列过滤是在发布端生成的所以发布端和订阅端的版本需要都支持这个特性跨大版本混合使用时需要提前确认。4.4 排序与COPY性能提升大数据量处理更香15在性能上也有官方明确提到的两点。第一内存排序性能提升官方说最多能到2倍。带LIMIT的排序查询、大数据量排序场景最能感受到差别。第二COPY的导入性能提升明显有测试显示大约能提升30%。实际效果和硬件、数据分布相关但每天要灌大批量文件的场景即便只提升20%对跑批作业的调度排班也有价值。升级后建议把最关键的批量导入SQL用EXPLAIN重新跑一遍确认执行路径变化。COPY依然是二进制版本之间公用的通用导入方式这个性能提升能让数据初始化、灾备恢复都跟着受益。4.5 正则函数、NULLS NOT DISTINCT与监控细节15增加了一批SQL标准正则函数包括regexp_count、regexp_instr、regexp_like、regexp_substr等。以前要在SQL里做文本匹配、提取和计数需要写substring加正则或自定义函数现在可以直接用函数实现比如SELECT regexp_count(abc123def456, [0-9]); SELECT regexp_instr(abc123def456, [0-9]); SELECT regexp_like(abc, a.c);NULLS NOT DISTINCT允许在唯一约束或唯一索引里把NULL视为“相同值”。以前一个列如果有多个NULL唯一索引不会有任何冲突15之后可以显式声明CREATE UNIQUE INDEX idx_account_no ON accounts (account_no) NULLS NOT DISTINCT;对业务上要求某个可选字段必须唯一的场景这是很实用的补丁。监控方面15新增了pg_stat_io视图能看到不同后端类型的读写次数和耗时排查IO瓶颈时比之前只能看pg_stat_bgwriter细致得多。另外wal_compression参数在15里默认开启对WAL体积大的系统能省一部分磁盘和网络开销代价是增加少量CPU开销。如果机器CPU本身就很紧张升级后要注意观察一下这个参数带来的变化。5. 从13到15升级路径与实战避坑5.1 直接升级还是分步升如果源库是13版本想到15官方pg_upgrade是支持直接跨版本升级的也就是13到15一步到位。不过中间隔了一个大版本我建议至少在测试环境完整跑一遍升级流程再上生产。pg_upgrade的典型流程是用新版本initdb初始化一个空数据目录然后通过新旧两个版本的bin目录把数据文件直接转换过去。速度快、停服窗口短。如果愿意可以加上--link选项用硬链接方式升级期间几乎不复制数据文件但升级后旧数据目录不能动。# 初始化新库 /usr/lib/postgresql/15/bin/initdb -D /var/lib/postgresql/15/data # 检查是否可升级 /usr/lib/postgresql/15/bin/pg_upgrade \ -b /usr/lib/postgresql/13/bin \ -B /usr/lib/postgresql/15/bin \ -d /var/lib/postgresql/13/data \ -D /var/lib/postgresql/15/data \ --check # 正式升级加 --link 可硬链接加速 /usr/lib/postgresql/15/bin/pg_upgrade \ -b /usr/lib/postgresql/13/bin \ -B /usr/lib/postgresql/15/bin \ -d /var/lib/postgresql/13/data \ -D /var/lib/postgresql/15/data \ --link另一种方式是逻辑复制滚动升级目标机初始化15实例源库创建发布目标库订阅全部追上后切换流量。这个方案的优点是停服时间短还能顺带做数据校验缺点也很明显逻辑复制不复制序列某些DDL和特殊类型需要额外处理准备成本高。对严格要求不停服的业务可以优先考虑这条路。5.2 升级前必做的检查清单我会在升级前按这个顺序过一遍备份pg_dump做逻辑备份pg_basebackup做物理备份尽量都留一份。备份不是流程仪式是最后一道保险。小版本对齐源库先升到13最新的小版本避免带着旧小版本的已知问题做跨大版本升级。插件检查第三方扩展和老的C函数是不是兼容目标版本。比如某些版本不支持的插件在新版本编译不过就要提前替换或评估影响。权限检查尤其关注15的public schema变化。虽然升级时旧库授权还在但新建逻辑库时一定会碰到。查询回归把核心业务的慢查询日志、批处理SQL收集起来在staging环境用新版本重跑对比执行计划和耗时。pg_upgrade --check先跑一次检查模式确认没有明显的文件权限、配置或插件冲突。这条清单看着繁琐但它能避免绝大多数升级翻车。我见过太多次升级失败是因为插件不兼容或者权限变化而不是版本本身的bug。5.3 场景化踩坑记录几个我在实际维护中遇到过、也帮别人排查过的问题场景一升级后应用无法在public schema建表。新版本新建的库public schema默认只有owner能写。应用账号用普通用户连接后报permission denied for schema public。处理方式就是前面说的显式GRANT最好在数据库初始化脚本里就写好别依赖默认值。场景二逻辑复制行列过滤没生效。如果你用15的订阅端去订阅13或14的发布端行列过滤是用不了的因为过滤在发布端生成。想让过滤生效发布端必须升级到15。跨版本混用时不要只看订阅端的功能列表要两端都确认版本。场景三并行vacuum在资源紧张的环境打满CPU。13开始支持并行vacuum后如果机器本身CPU核数多但磁盘IO弱配合默认autovacuum配置可能把资源吃满。建议在低峰期先观察可以把autovacuum_max_workers或autovacuum_vacuum_cost_delay调整到合适值。场景四执行计划变化导致慢查询。版本升级后因为统计信息、成本参数、优化器行为变化同一条SQL可能走不同执行计划。升级前收集慢查询升级后逐个对比是必须的功课。别上了生产才发现某个SQL从索引扫描变成了全表扫描。6. 写在后面的选型经验6.1 按场景选版本是不是必须追上最新版本不一定。如果业务稳定、负载不高、代码里还在大量使用老式写法13完全够用。但如果是新项目我会直接选择15或更新的版本因为MERGE、逻辑复制的行列过滤这些能力后期靠补丁是补不来的这次不升早晚要升。如果是因为性能和并发要升级14是一个很明显的水岭。从13升到14的收益通常比12升到13更突出大连接数、B-tree索引维护、子事务这三块都是底层硬优化。6.2 我测试版本差异的小习惯每一个版本我都会准备一个测试实例把三样东西跑一遍核心业务的慢查询、批量导入任务、一张大表的vacuum耗时。对比执行计划、WAL生成量、导入耗时和vacuum时长。版本差异这东西官方文档只能给方向硬件和业务形态不同最终都得用自己的数据说话。最后一件事升级窗口一定要避开业务高峰期哪怕staging环境测过一百遍生产环境的版本切换也要预留回滚时间。数据库版本升级不是安装一个新软件包它影响的是所有往数据库里写数据的系统。宁可慢一点也别在错误的时间窗口冒险。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑