这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 本章整理 PostgreSQL 高频面试题,并给出体现工程判断的回答框架。
32.1 MVCC 是什么?
回答要点:
- 每行有 xmin / xmax;
- 事务根据快照判断可见性;
- 读写互不阻塞;
- UPDATE 生成新版本;
- 旧版本由 VACUUM 清理。
加分项:解释长事务和复制槽如何阻止清理,导致表膨胀。
32.2 VACUUM 的作用?
回收死元组空间、冻结事务 ID、维护可见性映射、支持 Index Only Scan。普通 VACUUM 不归还磁盘空间;VACUUM FULL 会重写表并加重锁。
32.3 为什么索引不生效?
排查顺序:
- 索引类型与操作符是否匹配;
- 列是否被函数包装;
- 类型是否一致;
- 是否违背复合索引左前缀;
- 统计信息是否过期;
- 返回比例是否很高;
- 优化器成本判断。
32.4 EXPLAIN ANALYZE 看什么?
看 Seq / Index / Bitmap Scan、rows 与 actual rows、loops、Sort / Hash 内存、Buffers、Rows Removed。不要只看总耗时。
32.5 隔离级别如何选择?
默认 Read Committed 适合多数业务;报表一致性用 Repeatable Read;强一致并发约束用 Serializable,并接受重试成本。
32.6 WAL 的作用?
崩溃恢复、PITR、物理复制、归档。提交策略 synchronous_commit 决定性能与丢数据窗口。
32.7 复制槽为什么危险?
它会保留未被消费的 WAL。下游失效后可能把主库磁盘占满,并引发数据库保护性停止写入。必须监控 restart_lsn 和保留量。
32.8 逻辑复制与物理复制区别?
物理复制传 WAL 数据块变化,一致性强;逻辑复制解析逻辑变更,可按表选择和跨版本,但不复制 DDL、序列状态,且可能有冲突。
32.9 如何处理表膨胀?
先治因:长事务、autovacuum、复制槽、高频无效更新。严重时评估 pg_repack 或窗口内重建,VACUUM FULL 是最后选择。
32.10 B-Tree 和 GIN 如何选择?
B-Tree 适合等值范围排序;GIN 适合 JSONB 包含、数组、全文。GIN 查询强但写入维护成本高。
32.11 JSONB 如何优化?
包含查询建 GIN;高频字段提取生成列;需要范围排序时用表达式或普通列;控制文档大小和更新频率。
32.12 为什么需要连接池?
PostgreSQL 每连接一个进程,过多连接带来内存、调度和锁竞争。连接池复用进程,控制并发,但要理解事务级模式限制。
32.13 分区表注意事项?
分区键要匹配高频过滤;唯一约束包含分区键;提前创建分区;控制分区数量;查询验证分区剪枝。
32.14 如何做性能治理?
用 pg_stat_statements 建立慢 SQL 台账,用 EXPLAIN ANALYZE 定位,用索引和统计优化,用连接池和超时保护,最后压测和固化规范。
32.15 生产高可用方案?
主从复制 + 仲裁 + 自动切换 + 连接路由 + 监控告警 + 演练。明确 RPO/RTO,避免脑裂,旧主恢复前不得继续写。
本章小结
PostgreSQL 面试重点集中在 MVCC、VACUUM、索引、执行计划、WAL、复制、连接池和故障排查。回答时先讲机制,再讲风险和验证方式。
思考题
- 如何完整解释一次 UPDATE 的 MVCC 过程?
- 表膨胀的治理路径是什么?
- 逻辑复制升级要注意什么?
- JSONB 查询慢如何系统排查?
- 如何设计数据库的 RPO/RTO?