Skip to content

版本选择建议(LTS / 最新版) ​

本节回答一个非常实际的问题:生产环境到底该用哪个 PostgreSQL 版本? 学完本节,你将理解 PostgreSQL 为什么没有传统意义的 LTS,并掌握一套可落地的选版与升级窗口规划方法。

前置知识

阅读本节前,建议先了解:PostgreSQL vs MySQL vs Oracle

基础概念 ​

先澄清一个高频误解:PostgreSQL 社区不使用 "LTS" 这个说法。很多团队在选型文档里写"选 PostgreSQL 16 LTS",其实并不存在这样一个正式标记。

PostgreSQL 的策略是统一支持周期:每个主版本自首发日起获得 5 年官方支持,之后进入生命周期结束(EOL, End of Life)。由于每年发布一个大版本,任意时刻通常有 5 个左右主版本处于支持状态。这与"少数版本长期支持、其余短期支持"的 LTS 模式在机制上完全不同。

术语含义对选型的影响
主版本(Major Version)每年发布一次的大版本,如 16、17、18;含新功能,数据目录不兼容决定你能用哪些特性,也决定升级成本
次要版本(Minor Version)每年约 4 次的小版本,如 18.1~18.6;只修 bug 与安全问题无脑升级,必须跟随
支持周期(Support Lifecycle)主版本首发后 5 年决定"最晚什么时候必须升"
EOL支持期结束,此后不再有安全补丁生产环境不应停留在 EOL 版本
发布路线图(Roadmap)官方公布未来小版本的计划日期用于规划维护窗口
前沿版 vs 成熟版刚发布的大版本(前沿)与已发布 1~2 年的大版本(成熟)决定风险偏好

一句话总结选版原则:大版本可以慢一年,小版本必须快一个季度。

语法与用法 ​

选版不是凭感觉,先把"当前状态"量化出来。以下查询在任何 PostgreSQL 实例上都能直接运行。

sql
-- 1. 查看当前实例的完整版本与运行平台
SELECT version();
text
                                                 version
---------------------------------------------------------------------------------------------------------
 PostgreSQL 18.6 on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
(1 row)
sql
-- 2. 结构化版本号,便于程序判断与巡检脚本告警
SELECT
  current_setting('server_version')      AS version,       -- 如 18.6
  current_setting('server_version_num')  AS version_num,   -- 如 180006
  current_database()                     AS db_name;
text
 version | version_num | db_name
---------+-------------+---------
 18.6    |      180006 | demo
(1 row)
sql
-- 3. 判断是否还在官方支持期内(按 5 年支持期粗略折算)
--    维护一张版本 → EOL 日期的映射表比硬编码逻辑更可靠
WITH supported(version_num, eol_date) AS (
  VALUES
    (180000, DATE '2030-11-14'),
    (170000, DATE '2029-11-08'),
    (160000, DATE '2028-11-09'),
    (150000, DATE '2027-11-11'),
    (140000, DATE '2026-11-12')
)
SELECT
  s.version_num,
  s.eol_date,
  (s.eol_date - CURRENT_DATE) AS days_left,
  CASE
    WHEN s.eol_date < CURRENT_DATE THEN '已 EOL,立即升级'
    WHEN s.eol_date - CURRENT_DATE < 365 THEN '一年内 EOL,纳入升级计划'
    ELSE '支持期内'
  END AS advice
FROM supported AS s
WHERE s.version_num = (current_setting('server_version_num')::int / 10000) * 10000
ORDER BY s.eol_date;
text
 version_num |  eol_date  | days_left |     advice
-------------+------------+-----------+-----------------
      180000 | 2030-11-14 |      1515 | 支持期内
(1 row)
bash
# 4. 运维侧批量巡检:一次拿到所有集群的版本
pg_lsclusters
text
Ver Cluster Port Status Owner    Data directory              Log file
18  main    5432 online postgres /var/lib/postgresql/18/main  /var/log/postgresql/postgresql-18-main.log
15  main    5433 online postgres /var/lib/postgresql/15/main  /var/log/postgresql/postgresql-15-main.log
bash
# 5. 客户端工具版本应与服务端尽量一致(避免 pg_dump 版本过旧)
psql --version
pg_dump --version
pg_upgrade --version

详细说明 ​

为什么 PostgreSQL 没有传统 LTS ​

LTS(Long Term Support)模式的存在前提是"发布节奏不规律或支持成本需要分层"。Ubuntu、Node.js 之所以需要 LTS,是因为它们的版本发布频繁且支持层度需要区分。PostgreSQL 用另一种方式解决了同一个问题:

  • 固定 5 年支持期,而不是"选几个版本给更长支持"。
  • 每年一个大版本,节奏稳定,企业可以把它当作年度例行工作。
  • 小版本滚动发布,只含修复,升级无需改代码、不需要 dump/restore。
  • 主版本不兼容数据目录,所以"跨版本兼容"这件事本身就不成立,也就没必要用 LTS 来承诺它。

这带来的实践结论是:选"最新大版本减一"通常是最优解——既拿到了接近最新版本的能力与性能,又避开了新版本刚发布时的生态适配期。

各版本支持状态与推荐矩阵 ​

下表按维护时间表整理(EOL 日期以官方 Versioning Policy 为准):

主版本首次发布EOL定位生产建议
182025-092030-11最新稳定版新项目首选;含 AIO、Skip Scan、uuidv7() 等改进
172024-092029-11成熟稳定版保守团队首选;生态适配最充分
162023-092028-11成熟版可用,但新项目不建议作为起点
152022-102027-11即将进入后期存量系统可维持,规划升级
142021-092026-11临近 EOL一年内必须完成升级
13 及更早≤2020已 EOL停止维护立即评估升级,存在安全风险

按业务场景的选择建议:

场景推荐版本理由
全新项目、无历史包袱最新大版本(18)生命周期最长,可用新特性,避免上线即落后
全新项目、强合规要求最新大版本减一(17)生态与文档更充分,第三方扩展兼容性验证更久
存量系统升级目标锁定最新或减一一次到位,避免两年内再升一次
依赖特定扩展(PostGIS / pgvector / TimescaleDB)先查扩展兼容矩阵再定版本扩展对新版本的支持常滞后数周至数月
使用云托管服务跟随云厂商可选版本托管服务的可用版本列表通常滞后于社区
容器化部署官方镜像的 -alpine / -bookworm 具体标签避免使用 latest 标签导致不可控升级

小版本升级与主版本升级的区别 ​

这两类升级的运维动作完全不同,必须区分对待:

维度小版本升级(如 18.5 → 18.6)主版本升级(如 17 → 18)
数据目录兼容性完全兼容不兼容,无法直接启动
是否需要迁移数据不需要需要(pg_upgrade / 逻辑复制 / dump-restore)
停机时间秒级分钟到小时级
是否可能改 SQL 行为理论上不改变可能(废弃语法、默认值变化)
回退难度换回旧二进制即可极难,需依赖备份
建议频率跟随官方季度节奏每 2~3 年一次,提前半年规划
sql
-- 小版本升级前的安全检查:确认没有长事务阻塞关闭
SELECT pid, state, now() - xact_start AS xact_age, left(query, 60) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start
LIMIT 10;
sql
-- 升级前确认没有活动的复制槽(未使用的槽会拖住 WAL 清理)
SELECT slot_name, slot_type, active, restart_lsn
FROM pg_replication_slots
WHERE NOT active;

决定升级时机的信号 ​

不要等到 EOL 才行动。以下任一信号出现,就应把升级列入当季计划:

  • 距离当前版本 EOL 不足 12 个月。
  • 需要的某个扩展只支持新版本(例如新版本 PostGIS)。
  • 业务遇到了在新版本已被修复的性能或正确性问题。
  • 遇到安全漏洞但没有对应小版本可用(说明已 EOL)。
  • 云厂商明确通知该版本将下线。
  • 需要新版本独有的能力,例如声明式分区增强、MERGE、逻辑复制改进、向量索引性能。

版本与扩展的兼容性 ​

版本选择的约束往往不在数据库本身,而在扩展。升级前必须先确认扩展是否支持目标版本。

sql
-- 查看当前已安装的扩展及其版本
SELECT extname, extversion FROM pg_extension ORDER BY extname;
text
   extname    | extversion
--------------+------------
 pg_stat_statements | 1.11
 pg_trgm      | 1.6
 plpgsql      | 1.0
 postgis      | 3.5.0
(4 rows)
sql
-- 查看目标版本上有哪些扩展可用(需在目标版本实例中执行)
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name IN ('postgis', 'pgvector', 'pg_trgm', 'timescaledb')
ORDER BY name;
text
   name    | default_version | installed_version
-----------+-----------------+-------------------
 pg_trgm   | 1.6             | 1.6
 pgvector  | 0.8.0           |
 postgis   | 3.5.0           |
(3 rows)

实战示例 ​

场景:一套运行在 PostgreSQL 14.24 上的订单系统,数据量 800 GB,要求 2026 年内完成升级(14 将于 2026-11 EOL)。以下是完整的选版与升级决策过程。

第 1 步:确认现状

sql
SELECT
  current_setting('server_version')     AS version,
  current_setting('server_version_num') AS version_num,
  pg_size_pretty(pg_database_size(current_database())) AS db_size;
text
 version | version_num | db_size
---------+-------------+---------
 14.24   |      140024 | 812 GB
(1 row)

第 2 步:清点依赖,判断能否直接跳到最新版

sql
-- 已装扩展清单:任何一项不支持目标版本,都会阻塞升级
SELECT extname, extversion FROM pg_extension ORDER BY extname;
text
      extname      | extversion
-------------------+------------
 pg_stat_statements | 1.9
 pg_trgm            | 1.6
 plpgsql            | 1.0
(3 rows)
sql
-- 检查是否有已废弃语法依赖:以 text 上的 btree 排序差异为例
SELECT count(*) AS tables_with_text_index
FROM pg_index i
JOIN pg_class c ON c.oid = i.indrelid
JOIN pg_am am ON am.oid = i.indclass[0]
WHERE am.amname = 'btree';
text
 tables_with_text_index
------------------------
                     37
(1 row)

第 3 步:确定目标版本

结合本文矩阵:14 已临近 EOL,应一次跳到最新稳定版 18,避免两年内再升一次。

候选优点缺点结论
16生态成熟3 年后仍需升级放弃
17成熟且支持到 20292029 后仍需升级备选
18支持到 2030-11,生命周期最长新版本,需验证扩展采纳

第 4 步:在预发环境演练升级

bash
# 4.1 用 pg_upgrade --check 做无副作用的预检
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-datadir=/var/lib/postgresql/14/main \
  --new-datadir=/var/lib/postgresql/18/main \
  --old-bindir=/usr/lib/postgresql/14/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --check
text
Performing Consistency Checks
-----------------------------
Checking cluster versions                                   ok
Checking database user is the install user                  ok
Checking database connection settings                       ok
Checking for prepared transactions                          ok
Checking for new cluster tablespace directories             ok

*Clusters are compatible*
bash
# 4.2 预检通过后正式执行(--link 用硬链接缩短停机时间,--jobs 并行加速)
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-datadir=/var/lib/postgresql/14/main \
  --new-datadir=/var/lib/postgresql/18/main \
  --old-bindir=/usr/lib/postgresql/14/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --link --jobs=4

第 5 步:升级后重建统计信息并回归验证

sql
-- 5.1 校验版本与数据完整性
SELECT current_setting('server_version') AS version;

SELECT count(*) AS order_count FROM orders;
SELECT sum(amount) AS total_amount FROM orders;
text
 version
---------
 18.6
(1 row)

 order_count | total_amount
-------------+--------------
      428391 |  91538204.55
(1 row)
sql
-- 5.2 确认扩展在新版本下正常工作
SELECT extname, extversion FROM pg_extension ORDER BY extname;
SELECT similarity('机械键盘', '机械健盘') AS sim;   -- pg_trgm 生效即返回非 0
text
   extname    | extversion
--------------+------------
 pg_stat_statements | 2.1
 pg_trgm      | 1.6
 plpgsql      | 1.0
(3 rows)

   sim
---------
 0.666667
(1 row)
bash
# 5.3 收回旧版本的扩展与新集群统计信息
/usr/lib/postgresql/18/bin/vacuumdb --all --analyze-in-stages

第 6 步:保留观察期

bash
# 不要立即删除旧集群,保留 1~2 周以便问题回溯
# 确认无异常后,再回收 14 的数据目录与旧安装包
pg_lsclusters

注意事项 ​

  • 不要把 "LTS" 写进技术方案。PostgreSQL 没有 LTS 标记,写"选了 LTS 版本"会让评审与后续维护产生误解。正确表述是"选择当前支持期最长的主版本"。
sql
-- 错误!不存在这样的元数据,查询会报错
-- SELECT is_lts FROM pg_settings WHERE name = 'server_version';
  • 不要在主版本升级后继续使用旧版本的客户端工具。pg_dump/pg_restore/pg_upgrade 必须使用目标版本目录下的可执行文件。
bash
# 错误!用旧版 pg_dump 导出新库会因版本不符直接失败
# /usr/lib/postgresql/14/bin/pg_dump -d demo > demo.sql

# 正确:使用与服务端一致(或更新)的 pg_dump
/usr/lib/postgresql/18/bin/pg_dump -d demo -Fc -f demo.dump
  • 不要跨多个主版本"跳级"升级。pg_upgrade 只支持相邻或少数几个版本的直接升级,跨度过大时官方明确要求分步进行。若必须跨 3 个以上主版本,优先选逻辑复制方案。
text
不推荐:14 ──▶ 18(跨 4 个大版本,风险高、验证成本大)
推荐:  14 ──▶ 16 ──▶ 18(分两步,每步验证)
更优:  14 ──▶ 18 通过逻辑复制(无需停机,可随时回切)
  • 不要在业务高峰执行升级。即使 --link 把停机压到分钟级,升级后首次查询会因统计信息与缓存全冷而变慢,必须留出观察窗口。
  • 不要忽略 pg_upgrade 的 "check" 步骤。跳过预检直接执行,一旦中途失败,可能同时污染新旧两个数据目录。
  • 不要用 latest 镜像标签部署生产。官方 Docker 镜像的 latest 会随新主版本发布而变动,容器重建时可能无声完成主版本升级并导致数据目录不兼容。
  • 不要只关注数据库版本而忽略驱动与连接池。升级前需同步确认 JDBC/PG 驱动的版本要求、PgBouncer 对新版本协议的支持情况。
  • 不要忘记升级后的统计信息重建。pg_upgrade 在 18 会默认保留规划器统计信息,但跨大版本升级后仍建议主动执行一次 ANALYZE。

最佳实践 ​

  • 新项目直接采用当前最新稳定大版本(写作时为 18),一次投入换来最长支持期;若是核心金融系统,可选最新稳定版减一。
  • 把"最新大版本减一"作为保守团队的默认选择,兼顾能力、生态成熟度与支持期长度。
  • 小版本升级按季度例行执行,纳入自动化运维(打补丁 → 重启 → 健康检查),不设人工审批门槛。
  • 主版本升级提前 6 个月立项:预留扩展兼容验证、预发演练、业务回归测试与回滚方案设计的时间。
  • 优先选择逻辑复制做在线迁移:可做双向同步与灰度切流,回切成本远低于 pg_upgrade。
  • 建立版本台账:记录每个实例的版本、EOL 日期、升级负责人与下次复检时间,纳入季度巡检。
  • 把 EOL 日期写进监控告警:距离 EOL 少于 365 天时自动告警,而不是靠人记。
  • 为升级准备可验证的回滚方案:升级前的物理全备 + WAL 归档是底线,且必须在预发环境完整演练过一次恢复。
  • 版本收敛到 1~2 个主版本:多版本混布会显著增加运维、驱动兼容与知识共享成本。
  • 升级后做性能基线对比:用 pg_stat_statements 抓取升级前后的 Top-N 慢查询,确认没有回归。

下一节

继续学习:第一个 SQL 查询

参考链接 ​