一个数据库人的技术路线,往往不是从“我喜欢这项技术”开始,而是从一次故障、一次磁盘告急,或一场无法解释的生产事故开始。Shaun Thomas 与 PostgreSQL 的关系就是如此:早期因为膨胀、清理和升级问题而嫌弃它,后来却在真实生产环境中不断修复、自动化和扩展,最终成为 PostgreSQL 社区的长期贡献者。
这段经历最有价值的地方,不是某个神奇参数,而是它展示了数据库能力如何在压力下形成:从操作员视角理解存储引擎,再把一次性的救火动作沉淀为脚本、HA 架构、舰队管理工具和公开知识。
从索引膨胀开始,理解数据库版本的时代差异
早期 PostgreSQL 的维护体验,与今天并不是同一个世界。文章回顾了 PostgreSQL 7.x 时代的一个问题:表经过 VACUUM FULL 变小之后,索引却可能继续膨胀。为应对这种情况,作者改造了维护脚本,定期重建数据库中的索引,甚至曾经每两个小时执行一次。
这类做法放到今天不能简单照搬。高频全库 REINDEX 会产生额外 I/O、锁竞争和 WAL,应该先确认版本、索引类型、业务窗口以及膨胀证据。更重要的经验是:维护动作必须建立在可观测性上,而不是建立在“大家都这么做”的传闻上。
另一个典型例子是 PostgreSQL 7.4 的 max_fsm_pages。这个参数限制了空闲空间映射能够追踪的页面数量。当高负载数据库更新范围超过这个上限时,系统可能持续遗忘旧页面,最终形成不断扩大的膨胀。作者通过按表大小排序、从小表开始执行清理,让释放出来的空间支持后续更大表的维护,最终把数据库体积压缩到原来的约十分之一。
今天这个参数已经不存在了,但问题意识仍然适用:
- 遇到磁盘告警,先判断空间是数据、索引、WAL、临时文件还是日志。
- 不要把“执行维护命令”当成完整方案,还要考虑锁、失败恢复和业务吞吐。
- 版本升级前,必须重新审视旧版本遗留的参数和运维脚本。
从 1 TB 交易库中提炼生产 DBA 方法论
2010 年,作者进入一家金融公司,面对的是一个完全不同量级的系统:约 1 TB 的 PostgreSQL 兼容数据库,Java/Hibernate 应用,交易高峰约 15,000 TPS,连接数可能达到 1,000,实例还会频繁崩溃。后续业务峰值更达到约 35,000 TPS。
这里最值得借鉴的不是某一组“最佳参数”,而是排查顺序。作者先处理恢复和脱敏流程,再处理崩溃与存储延迟,最后构建高可用和集群管理能力。这样做避免了在一个不可恢复、不可验证的系统上盲目调优。
他发现多个配置仍处于工厂默认值或不适合生产规模的状态,例如工作内存、维护内存、自动清理相关设置、检查点间隔和检查点完成目标等。调整这些参数后,系统稳定性明显改善;当 RAID-10 在缓存变冷后仍无法满足交易延迟时,又通过 NVMe 设备解决了冷启动和随机 I/O 瓶颈。
可以把这套思路概括为三层:
- 先保证可恢复:备份、恢复、脱敏流程必须能稳定运行,并且有人定期验证。
- 再保证可运行:修复明显错误的参数、连接管理和检查点行为,消除频繁崩溃。
- 最后优化性能:用指标证明瓶颈在哪里,再决定是换存储、改 SQL、调整连接池还是重构架构。
一个可改造的 PostgreSQL 巡检与维护示例
下面的示例适合改造成日常巡检脚本。它不会自动执行高风险维护,而是先输出数据库规模、表膨胀线索和当前连接情况。运行前请把连接参数改成目标环境,并确保账号有读取系统视图的权限。
#!/usr/bin/env bash
set -euo pipefail
: "${PGHOST:=127.0.0.1}"
: "${PGPORT:=5432}"
: "${PGDATABASE:=appdb}"
: "${PGUSER:=postgres}"
export PGHOST PGPORT PGDATABASE PGUSER
echo '== Database size =='
psql -X -v ON_ERROR_STOP=1 -c \
"SELECT current_database() AS database,
pg_size_pretty(pg_database_size(current_database())) AS size;"
echo
echo '== Largest tables and indexes =='
psql -X -v ON_ERROR_STOP=1 -c \
"SELECT n.nspname AS schema_name,
c.relname AS table_name,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,
pg_size_pretty(pg_relation_size(c.oid)) AS table_size,
pg_size_pretty(pg_indexes_size(c.oid)) AS index_size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 20;"
echo
echo '== Connections by state =='
psql -X -v ON_ERROR_STOP=1 -c \
"SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state
ORDER BY count(*) DESC;"
如果巡检确认某个索引确实需要重建,可以在明确维护窗口后单独处理。高并发业务通常优先评估 REINDEX CONCURRENTLY,但它仍会消耗资源、生成 WAL,并且存在额外磁盘需求;在执行前要检查剩余空间和版本支持情况:
-- 先确认目标索引,不要直接对整个数据库执行重建
SELECT schemaname, indexname, indexdef
FROM pg_indexes
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY schemaname, indexname;
-- 示例:确认业务低峰期后执行
REINDEX INDEX CONCURRENTLY public.orders_created_at_idx;
这些命令只是安全的起点,不是自动化维护策略。生产环境还应记录执行时间、锁等待、WAL 增长、磁盘余量和业务延迟,并准备回滚或中止方案。
高可用不是一个开关,而是一组可验证的组件
在 PostgreSQL 9.0 的流复制出现之前,作者曾使用 DRBD、LVM、Corosync、Pacemaker 和虚拟 IP 构建高可用栈:块设备负责复制,集群管理器负责资源编排,VIP 指向当前主节点,额外的 gratuitous ARP 用于刷新网络设备的路由缓存。
这套架构体现了一个重要原则:高可用系统必须定义组件的启动、停止、依赖和故障转移顺序。仅仅“有两台机器”并不等于高可用。还需要回答:
- 谁判断主库已经失效?
- 谁负责提升备库?
- 应用如何发现新的主库地址?
- 旧主库恢复后如何防止脑裂?
- 备份和恢复是否在切换后仍然有效?
今天可以使用 Patroni、pg_auto_failover 或云厂商托管能力,但这些工具并不会替团队承担设计责任。选型时仍要测试网络分区、磁盘故障、进程假死、节点重启和旧主库回归等场景。
从一次次救火,到工具和知识资产
当 PostgreSQL 集群数量增长到约十余套时,逐台检查健康状态、备份、切换和重建已经无法依靠人工完成。作者因此构建了 ElepHaaS,通过状态代理向中央协调器推送信息,再结合 Salt 初始化节点,实现集群舰队级管理。
这说明自动化的价值不只在于“少敲几条命令”,更在于把运维动作变成一致、可审计、可重复的流程。一个成熟的 PostgreSQL 平台至少应逐步具备:
- 统一的版本、参数和扩展清单;
- 备份成功率、恢复耗时和最近一次恢复验证记录;
- 主备角色、复制延迟和故障转移历史;
- 可重复执行的切换、重建和节点初始化流程;
- 面向开发团队的 SQL 反模式与性能案例库。
作者后来把内部 Confluence 上的经验文章公开,形成了 PG Phriday,并进一步通过书籍、演讲、工具和社区活动回馈 PostgreSQL 生态。这条路径很典型:生产事故迫使人理解底层机制,底层机制又促使人编写工具,工具和案例最终沉淀为社区知识。
给正在维护 PostgreSQL 的团队的建议
PostgreSQL 的历史提醒我们,今天看似理所当然的功能,过去可能需要大量脚本和运维技巧才能实现。因此,不要把旧文章中的命令原样搬到新版本,也不要因为现代工具更方便就跳过基础验证。
可以用下面这份清单开始改进:
- 先确认版本:记录 PostgreSQL、扩展、操作系统和存储设备版本。
- 先验证恢复:备份文件存在不代表能恢复,定期做真实恢复演练。
- 用数据定位瓶颈:查看锁、慢查询、缓存命中率、WAL、检查点、复制延迟和 I/O 延迟。
- 把危险操作变成审批流程:
VACUUM FULL、REINDEX、参数修改和故障转移都应有窗口与记录。 - 自动化重复动作:切换、巡检、初始化和重建要能重复执行,并且失败时给出明确状态。
- 分享可复用经验:一篇带有症状、证据、修复和边界条件的文章,往往比一次口头交接更有价值。
从“Postgres 很糟”到“Postgres 值得投入二十年”,变化的不只是数据库本身,也包括工程师理解问题、验证假设和服务社区的方式。工具会越来越成熟,AI 也会让许多答案更容易获得;但在生产环境中判断风险、权衡取舍并确保系统真的能恢复,仍然需要有人站在全局视角负责。