跳到主要内容

PostgreSQL 架构

问题

PostgreSQL 的整体架构是什么?与 MySQL 线程模型有什么区别?VACUUM 是什么?

答案

一、进程模型

PostgreSQL 采用 多进程 架构(MySQL 是多线程):

组件说明
Postmaster主进程,监听连接,fork 子进程
Backend每个连接一个进程(非线程)
Background Writer定期将脏页写入磁盘
WAL Writer将 WAL 缓冲区写入磁盘
Autovacuum自动清理死元组
Checkpointer执行检查点,刷脏页
进程 vs 线程
  • PostgreSQL 多进程:稳定性好(一个进程崩溃不影响其他),但内存开销大,连接数受限
  • MySQL 多线程:内存效率高,连接切换快,但线程安全问题复杂

实践:PostgreSQL 通常配合 PgBouncer 等连接池使用,避免频繁创建进程。

二、内存架构

内存区域说明类比 MySQL
Shared Buffers共享缓冲池,缓存数据页Buffer Pool
WAL BuffersWAL 日志缓冲区Log Buffer
Work Mem排序、哈希操作的内存sort_buffer_size
Maintenance Work MemVACUUM、CREATE INDEX 内存
Temp Buffers临时表使用的内存tmp_table_size

三、MVCC 实现(与 MySQL 的关键区别)

PostgreSQL 的 MVCC

PostgreSQL 不使用 undo log。每次 UPDATE 都会 创建一个新的行版本旧版本保留在原表中

┌─────────────────────────────────┐
│ 堆表(Heap) │
│ id=1, name='张三', xmin=100, xmax=200 ← 旧版本(已死) │
│ id=1, name='李四', xmin=200, xmax=0 ← 新版本(活跃) │
└─────────────────────────────────┘
字段含义
xmin创建该行版本的事务 ID
xmax删除(或更新)该行版本的事务 ID
ctid行的物理位置(页号, 偏移量)

对比 MySQL 和 PostgreSQL 的 MVCC

对比项MySQL InnoDBPostgreSQL
旧版本存储undo log(单独空间)堆表中(与新数据同表)
新版本原地更新写新行,标记旧行
清理机制purge 线程清理 undoVACUUM 清理死元组
索引更新非主键只需更新 undo 指针所有索引都要更新(指向新行)
优势UPDATE 密集型性能好实现简单,快照隔离强
劣势长事务导致 undo 膨胀表膨胀,需要 VACUUM

四、VACUUM 机制

由于旧版本行保留在表中,需要 VACUUM 来清理 死元组(Dead Tuples)

VACUUM 的类型

类型操作说明
VACUUM标记死元组空间可复用不阻塞读写空间不归还 OS
VACUUM FULL重建整张表排他锁空间归还 OS,但期间表不可用
Autovacuum自动定期执行 VACUUM不阻塞推荐开启
表膨胀

如果 VACUUM 跟不上 UPDATE/DELETE 的速度,或者有长事务阻止 VACUUM 回收旧版本,表会持续膨胀。

预防

  • 不要有长事务(尤其是空闲的 idle in transaction
  • 合理配置 autovacuum 参数
  • 监控 pg_stat_user_tables.n_dead_tup

五、WAL(Write-Ahead Logging)

PostgreSQL 的 WAL 等价于 MySQL 的 redo log,用于崩溃恢复:

数据修改 → 写 WAL buffer → fsync 到 WAL 文件 → 修改 Shared Buffer 中的页

WAL 还用于:

  • 流复制:将 WAL 发送给从库实现主从复制
  • PITR:基于 WAL 的任意时间点恢复
  • 逻辑复制:解析 WAL 生成逻辑变更事件

常见面试问题

Q1: PostgreSQL 为什么需要 VACUUM 而 MySQL 不需要?

答案
因为 MVCC 实现方式不同:

  • MySQL:旧版本存在 undo log 中,purge 线程自动清理,主表数据不会膨胀
  • PostgreSQL:旧版本直接存在堆表中,必须通过 VACUUM 回收空间

Q2: 为什么 PostgreSQL 选择多进程而不是多线程?

答案
历史原因和设计哲学:

  • 1996 年 PostgreSQL 项目启动时,线程模型还不成熟
  • 进程隔离性更好,一个连接崩溃不会影响整个数据库
  • 配合连接池(PgBouncer),进程模型在实际使用中性能也不差

Q3: shared_buffers 应该设置多大?

答案
一般推荐为系统内存的 25%。例如 64GB 内存的服务器,设置 16GB。不建议超过 40%,因为 PostgreSQL 还依赖操作系统的文件缓存。


相关链接