跳到主要内容

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 还依赖操作系统的文件缓存。


相关链接​