3. 数据库基础:关系型数据库 ACID 特性、常见数据库(MySQL、PostgreSQL)与缓存的关系

在构建缓存一致性方案(如 TCC 模式)之前,必须深刻理解关系型数据库的底层保障机制。数据库的 ACID 特性决定了事务的边界与可靠性,而不同数据库的实现差异直接影响缓存策略的设计。本章将拆解 ACID 的核心含义,对比 MySQL 与 PostgreSQL 在事务隔离与持久化上的关键区别,并建立数据库与缓存之间的协作模型。

3.1 ACID 特性:事务的四大基石

ACID 是关系型数据库事务正确执行的四个基本属性。任何缓存一致性方案,最终都要依赖数据库事务的原子性和持久性来兜底。

特性 全称 核心含义 缓存场景关联
A Atomicity(原子性) 事务内的所有操作要么全部成功,要么全部回滚 TCC 的 Confirm/Cancel 必须依赖原子性保证业务状态不部分更新
C Consistency(一致性) 事务执行前后,数据库完整性约束不被破坏 缓存数据必须与数据库满足最终一致性,不能出现违反业务规则的数据
I Isolation(隔离性) 并发事务之间互不干扰,隔离级别控制可见性 隔离级别影响缓存读取到的数据版本,脏读/不可重复读会破坏缓存一致性
D Durability(持久性) 已提交的事务结果永久保存,不因故障丢失 缓存失效后,持久化数据是重建缓存的唯一可靠来源
关键认知: 缓存一致性方案不能替代数据库的 ACID 保障,而是建立在 ACID 之上的“最终一致性”协调层。数据库的持久性是缓存恢复的底线,隔离性决定了缓存何时能看到最新数据。

3.2 隔离级别与缓存可见性

隔离级别直接决定了事务之间数据的可见范围。在缓存场景中,如果数据库隔离级别过低,缓存可能读到“脏数据”或“不可重复读”的数据版本,导致缓存与数据库长期不一致。

隔离级别 脏读 不可重复读 幻读 缓存风险
READ UNCOMMITTED 可能 可能 可能 缓存可能读到未提交的脏数据,极高风险
READ COMMITTED 避免 可能 可能 缓存可能读到已提交但随后被回滚的数据(间隙问题)
REPEATABLE READ 避免 避免 可能(MySQL InnoDB 通过间隙锁避免) 缓存一致性较好,但快照读可能导致缓存延迟更新
SERIALIZABLE 避免 避免 避免 缓存一致性最强,但并发性能极低,不适合高吞吐
实践建议: 大多数缓存一致性方案选择 READ COMMITTED 或 REPEATABLE READ 作为数据库隔离级别。READ COMMITTED 配合乐观锁(版本号)可以兼顾性能与一致性;REPEATABLE READ 适合对一致性要求更高的金融类场景。

3.3 MySQL 与 PostgreSQL:事务实现差异

MySQL(InnoDB)和 PostgreSQL 是当前最主流的两款关系型数据库,它们在事务实现、MVCC 机制和持久化策略上存在显著差异,这些差异直接影响缓存一致性方案的设计选择。

对比维度 MySQL (InnoDB) PostgreSQL
MVCC 实现 基于回滚段(undo log)+ 聚簇索引中的隐藏列(DB_TRX_ID, DB_ROLL_PTR) 基于堆表 + 可见性映射(Visibility Map)+ 事务快照(Snapshot)
默认隔离级别 REPEATABLE READ(但通过间隙锁避免幻读) READ COMMITTED
事务快照 语句级快照(REPEATABLE READ 下为事务级快照) 事务级快照(每个事务开始时获取全局快照)
持久化机制 双写缓冲(Doublewrite Buffer)+ Redo Log + Binlog WAL(Write-Ahead Log)+ Full Page Writes
主从复制 基于 Binlog(Row/Statement/Mixed) 基于 WAL 的流复制(Streaming Replication)
锁机制 行锁 + 间隙锁(Gap Lock)+ 临键锁(Next-Key Lock) 行锁 + 谓词锁(Predicate Lock)+ 轻量级锁
缓存设计注意: MySQL 的 REPEATABLE READ 下,快照读不会看到其他事务已提交的最新数据,这可能导致缓存更新滞后。PostgreSQL 的 READ COMMITTED 下,每次查询都能看到最新已提交数据,更适合缓存实时性要求高的场景,但需要额外处理不可重复读问题。

3.4 数据库与缓存的协作模型

数据库与缓存不是替代关系,而是分层协作关系。数据库负责持久化与事务保障,缓存负责加速读取与减轻数据库压力。理解两者的协作模型,是设计缓存一致性方案的前提。

数据库与缓存分层协作模型 应用服务 缓存层(Redis/Memcached) 数据库层(MySQL/PostgreSQL) 读请求(先查缓存) 写请求 / 缓存未命中 缓存一致性同步(TCC / 双写 / 订阅 Binlog) 持久化存储(WAL / Redo Log) 事务提交 -> 持久化 核心原则 数据库是唯一权威数据源(Source of Truth),缓存是加速副本(Cache Copy)

3.5 缓存一致性方案对数据库的依赖

不同的缓存一致性方案对数据库能力的要求不同。下表总结了常见方案与数据库特性的依赖关系:

缓存一致性方案 依赖的数据库特性 MySQL 支持度 PostgreSQL 支持度
Cache-Aside(旁路缓存) 事务原子性(避免部分更新) ✅ 良好 ✅ 良好
Read-Through / Write-Through 事务隔离性(避免脏读) ✅ 良好 ✅ 良好
Write-Behind(异步回写) 持久性 + 主从复制(数据不丢) ✅ 需配置双1参数 ✅ 需配置同步复制
Binlog 订阅(Canal / Debezium) 行级变更日志(Row-Based Binlog / WAL) ✅ 原生支持 Row 格式 ✅ 原生支持逻辑解码
TCC 模式(本课程核心) 原子性 + 隔离性 + 持久性 ✅ 需配合事务表 + 幂等性设计 ✅ 需配合 SAVEPOINT 或子事务
TCC 模式对数据库的特殊要求: TCC 的 Try、Confirm、Cancel 三个阶段都需要数据库事务的原子性来保证状态不丢失。同时,隔离性决定了 Try 阶段预留的资源对其他事务是否可见。MySQL 的 REPEATABLE READ 配合间隙锁可以防止幻读,但会降低并发;PostgreSQL 的 READ COMMITTED 配合行锁更适合高并发 TCC 场景。

3.6 数据库选型对缓存策略的影响

选择 MySQL 还是 PostgreSQL,不仅影响数据库层的设计,还会对缓存一致性策略产生连锁反应:

  • MySQL 场景: 适合读多写少、对缓存实时性要求不极端、依赖 Binlog 订阅做缓存更新的业务。MySQL 的 REPEATABLE READ 默认隔离级别下,缓存更新需要额外处理快照读延迟问题。
  • PostgreSQL 场景: 适合写密集、对缓存实时性要求高、需要复杂事务嵌套(如 TCC 子事务)的业务。PostgreSQL 的 READ COMMITTED 默认隔离级别让缓存更容易读到最新数据,但需要处理不可重复读带来的缓存版本跳跃。
  • 混合场景: 部分大型系统采用 MySQL 做核心交易库,PostgreSQL 做分析库或配置库,缓存层统一对接 Redis,通过不同的同步策略适配两种数据库的事务特性。
选型建议: 如果团队对 MySQL 运维经验丰富,优先选择 MySQL + Binlog 订阅方案;如果团队追求更强的事务一致性和更灵活的并发控制,PostgreSQL 是更好的选择。TCC 模式在两种数据库上都能实现,但 PostgreSQL 的 SAVEPOINT 机制让 Cancel 阶段的回滚更优雅。

3.7 本章小结