PostgreSQL WAL 与 CDC 详解

一、WAL 是什么

WAL(Write-Ahead Logging,预写日志)是 PostgreSQL 的持久化核心机制

核心原则:任何数据修改,必须先把变更写入日志文件,再修改实际数据页。


二、WAL 解决了什么问题

没有 WAL 的世界

复制代码
UPDATE user SET name='Alice' WHERE id=1

步骤:
1. 把数据页从磁盘加载到内存 (Shared Buffer)
2. 修改内存中的数据页
3. 将数据页写回磁盘

问题:如果第 3 步执行到一半机器断电 → 数据页写了一半 → 数据永久损坏

有了 WAL

复制代码
UPDATE user SET name='Alice' WHERE id=1

步骤:
1. 把数据页加载到内存
2. 修改内存中的数据页
3. ✅ 先把变更写入 WAL 文件(顺序写,极快)
4. 再把数据页写回磁盘(checkpoint 时才做)

崩溃恢复:重启后 replay WAL 日志 → 数据恢复到一致状态

三、WAL 的存储结构

复制代码
$PGDATA/pg_wal/
  000000010000000000000001   ← 每个文件默认 16MB
  000000010000000000000002
  000000010000000000000003
  ...

WAL Record 内容

复制代码
┌─────────────────────────────────────────────────────┐
│  LSN (日志序列号)  │  操作类型  │  表OID  │  变更数据  │
│  0/1234ABCD       │  UPDATE    │  16384  │  old + new │
└─────────────────────────────────────────────────────┘
  • LSN(Log Sequence Number):单调递增,唯一标识每条日志位置
  • 操作类型:INSERT / UPDATE / DELETE / COMMIT 等
  • 变更数据before(旧值)+ after(新值)

四、WAL 的三大用途

复制代码
WAL 文件
  │
  ├── 1. 崩溃恢复
  │      重启后 replay 未持久化的变更
  │      保证 ACID 中的 D(Durability 持久性)
  │
  ├── 2. 流复制(Streaming Replication)
  │      主库 WAL → 实时推送给从库
  │      从库 replay → 保持数据同步
  │      实现高可用 / 读写分离
  │
  └── 3. Logical Decoding(逻辑解码)
         解析 WAL 中的变更为结构化事件
         供 CDC 工具(Debezium 等)消费
         实现实时数据同步

五、WAL 的两种级别

级别 配置 说明
replica(默认) wal_level = replica 支持流复制,不支持逻辑解码
logical wal_level = logical 在 replica 基础上,额外记录逻辑变更信息,供 CDC 使用

开启 CDC 前,DBA 需要确认:

sql 复制代码
SHOW wal_level;
-- 需要返回 logical

六、CDC 是什么

CDC(Change Data Capture,变更数据捕获) 是一种捕获数据库中数据变更并将变更实时传递给下游系统的技术。

核心思想

复制代码
不扫全表,只捕获「发生了什么变化」

传统全量同步:SELECT * FROM table → 每次全量扫描
CDC 增量同步:监听日志 → 只传递 INSERT/UPDATE/DELETE 事件

CDC 捕获的事件格式

json 复制代码
{
  "op": "u",
  "before": {
    "id": 1,
    "name": "Bob"
  },
  "after": {
    "id": 1,
    "name": "Alice"
  },
  "source": {
    "table": "user",
    "ts_ms": 1718600000000
  }
}

七、CDC 的作用

场景 说明
数据库同步 DB → Elasticsearch / Redis / 其他数据库 实时同步
微服务解耦 一个服务的数据变更,自动通知其他服务
审计日志 记录谁、什么时候、改了什么
缓存失效 DB 数据变更 → 自动淘汰对应缓存
数仓 ETL 增量同步到数据仓库,替代定时全量抽取

八、基于 WAL 的 CDC 架构

复制代码
PostgreSQL
  └── WAL (wal_level=logical)
        │
        │  Logical Replication Slot
        │  (PG 保留 WAL 直到 slot 消费完)
        ▼
     Debezium Connector
     (读取并解析 WAL logical decoding 输出)
        │
        ▼
     Kafka Topic
     (salesdata.public.salesdata_master_info)
        │
        ├──▶ Kafka Connect ES Sink → Elasticsearch
        ├──▶ Consumer App          → 业务处理
        └──▶ Kafka Connect JDBC    → 其他数据库

与当前项目的对比

当前方案(全量扫表) CDC 方案
触发方式 定时任务 数据变更实时触发
同步范围 全量(百万级) 仅变更的行(极少)
内存压力 高(OOM 风险) 极低
延迟 分钟级 毫秒级
代码侵入 零侵入
漏数据风险 有(时间窗口问题) 无(日志不丢)

九、Replication Slot 注意事项

sql 复制代码
-- 查看当前 slot
SELECT slot_name, plugin, active, restart_lsn FROM pg_replication_slots;

-- ⚠️ 风险:如果 Debezium 停止消费,PG 会保留 WAL 不删除
-- WAL 文件积累 → 磁盘撑爆
-- 建议:监控 pg_replication_slots 中的 lag

十、小结

复制代码
WAL 是 PostgreSQL 的基础设施
  ├── 保证数据不丢(崩溃恢复)
  ├── 实现高可用(流复制)
  └── 支撑 CDC(Logical Decoding)
        └── Debezium 利用 CDC
              └── 实现 PG → Kafka → ES 实时同步
                    解决全量扫表 OOM 问题