PostgreSQL及MySQL数据库日志机制深度对比
一、结论先行
PostgreSQL 没有 redo log、binlog、undo log。
只有 WAL 一套日志,承担了 MySQL 三套日志的全部职责。
二、MySQL 为什么需要三套日志
MySQL 的架构分为两层,导致日志也分裂成两层:
MySQL 架构 ┌─────────────────────────────────────┐ │ Server 层 │ │ binlog(引擎无关,Server 自己记) │ ├─────────────────────────────────────┤ │ InnoDB 引擎层 │ │ redo log(InnoDB 自己记) │ │ undo log(InnoDB 自己记) │ └─────────────────────────────────────┘
- binlog:Server 层的逻辑日志,记录"执行了什么操作"
- redo log:InnoDB 的物理日志,记录"数据页哪里改了"
- undo log:InnoDB 的回滚日志,记录"改之前是什么"
这是历史包袱:MySQL 最初是插件式引擎架构,Server 层不知道引擎内部,只能各记各的。
三、三套日志各自的职责
3.1 redo log —— 崩溃恢复
写入时序:
事务提交
↓
写 redo log(WAL,顺序写,快)
↓
内存数据页标记为 dirty
↓
checkpoint 时才把 dirty page 刷盘
崩溃恢复:
重启 → 读 redo log → replay 未刷盘的变更 → 数据一致
对应 PostgreSQL:WAL 承担了完全相同的职责
3.2 binlog —— 主从复制 / 数据归档 / CDC
binlog 有三种格式: STATEMENT:记录 SQL 语句(不精确,有歧义) ROW: 记录行的前后变化(精确,CDC 必用) MIXED: 混合模式 主从复制流程: 主库 binlog → IO Thread → 从库 relay log → SQL Thread → 从库执行
对应 PostgreSQL:WAL logical decoding 承担了相同职责
3.3 undo log —— 事务回滚 + MVCC
UPDATE user SET name='Alice' WHERE id=1 InnoDB 做法: 1. 把旧值 name='Bob' 写入 undo log 2. 数据页改为 name='Alice'(原地更新) 3. 事务回滚时:从 undo log 读旧值,恢复回去 MVCC 读旧版本: 另一个事务需要读 name='Bob'(旧快照) → 从 undo log 回溯旧版本
对应 PostgreSQL:不需要 undo log,见下节
四、PostgreSQL 为什么不需要 undo log
这是 PG 和 MySQL MVCC 实现思路的根本差异。
MySQL:原地更新(Update in Place)
数据页: ┌──────────────────┐ │ id=1, name=Alice │ ← 只存最新版本 └──────────────────┘ undo log: ┌──────────────────┐ │ id=1, name=Bob │ ← 旧版本存这里 └──────────────────┘ 读旧快照 → 去 undo log 里找
PostgreSQL:追加写(Append-Only Heap)
数据页: ┌──────────────────────────────────────────────┐ │ Tuple1: id=1, name=Bob [xmax=200, dead] │ ← 旧版本,打标记 │ Tuple2: id=1, name=Alice [xmin=200, alive] │ ← 新版本 └──────────────────────────────────────────────┘ UPDATE = INSERT 一条新 tuple + 把旧 tuple 标记为 dead 读旧快照 → 直接读同一数据页里的 dead tuple(根据 xmin/xmax 判断可见性)
代价:需要 VACUUM 清理
dead tuple 不会自动消失,需要 VACUUM 定期清理: VACUUM table_name; -- 或者 autovacuum 后台自动运行 如果 VACUUM 跑不过来(写入太快)→ 表膨胀(Table Bloat) 这是 PG 的独有问题,MySQL 没有
五、MySQL 的两阶段提交
由于 redo log 和 binlog 是两套独立的日志,MySQL 必须用两阶段提交保证一致性:
事务提交流程: 1. InnoDB 写 redo log(prepare 阶段) 2. Server 写 binlog 3. InnoDB 提交 redo log(commit 阶段) 如果第 2 步崩溃: 重启后发现 redo log prepare 但 binlog 没有 → 回滚 如果第 3 步崩溃: 重启后发现 binlog 有但 redo log 未 commit → 提交 保证 redo log 和 binlog 始终一致
PostgreSQL 没有这个问题,因为只有一套 WAL,天然一致。
六、完整对比表
| 职责 | MySQL | PostgreSQL |
|---|---|---|
| 崩溃恢复 | redo log(InnoDB 层) | WAL |
| 主从复制 | binlog(Server 层) | WAL Streaming Replication |
| CDC / 变更捕获 | binlog(ROW 格式) | WAL Logical Decoding |
| 事务回滚 | undo log | WAL(未提交事务直接丢弃) |
| MVCC 旧版本 | undo log | 数据页内的 dead tuple |
| 日志套数 | 3 套 | 1 套 |
| 架构复杂度 | 高(两阶段提交协调) | 低 |
七、各自的优缺点
PostgreSQL WAL 优点
✅ 架构简洁,一套日志全包 ✅ 无两阶段提交开销 ✅ WAL 顺序写性能极好 ✅ Logical Decoding 开箱即用,CDC 友好
PostgreSQL WAL 缺点 / 注意事项
⚠️ dead tuple 堆积需要 VACUUM,写入密集场景需要调优 ⚠️ wal_level=logical 会增加 WAL 体积(记录更多信息) ⚠️ Replication Slot 如果消费方停止,WAL 文件不删除 → 磁盘风险
MySQL 日志优点
✅ undo log 独立,旧版本存储与数据文件分离,表不膨胀 ✅ binlog 格式成熟,生态丰富(Canal、Maxwell 等 CDC 工具)
MySQL 日志缺点
⚠️ 三套日志,维护复杂 ⚠️ 两阶段提交有额外开销 ⚠️ binlog 和 redo log 可能不一致(极端崩溃场景) ⚠️ undo log 过大会影响读性能(长事务问题)
八、小结
MySQL:历史包袱导致三套日志 redo log ← 崩溃恢复 binlog ← 复制/CDC undo log ← 回滚/MVCC PostgreSQL:设计简洁,一套 WAL 全包 WAL ← 崩溃恢复 + 复制 + CDC heap dead tuple ← MVCC 旧版本(替代 undo log) 代价不同: MySQL → 两阶段提交协调开销 + undo log 长事务问题 PG → dead tuple 堆积需要 VACUUM 调优
总结
到此这篇关于PostgreSQL及MySQL数据库日志机制深度对比的文章就介绍到这了,更多相关PgSQL MySQL日志机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论