3、传统单体事务模型:ACID特性、本地事务与全局事务、在事件风暴画布中建模单体事务

在分布式事务架构演进中,单体事务模型是理解后续所有复杂模式的基石。本章将系统梳理ACID理论、本地事务与全局事务的差异,并展示如何在事件风暴画布中为单体应用建立事务边界。

3.1 ACID特性:事务的四大黄金法则

ACID是数据库事务正确执行的四个基本特性,由Haerder与Reuter在1983年正式提出。任何事务处理系统都必须满足这四项约束,才能保证数据的一致性与可靠性。

特性 全称 核心含义 典型实现机制
A Atomicity(原子性) 事务中的所有操作要么全部成功提交,要么全部失败回滚,不存在中间状态 Undo日志、Redo日志、事务日志
C Consistency(一致性) 事务执行前后,数据库必须满足所有预定义的完整性约束(主键、外键、唯一约束、业务规则) 约束检查、触发器、应用层校验
I Isolation(隔离性) 并发执行的事务之间互不干扰,每个事务都感觉自己是唯一运行的 锁机制、MVCC(多版本并发控制)
D Durability(持久性) 一旦事务提交成功,其对数据库的修改就是永久性的,即使系统崩溃也不会丢失 WAL(预写式日志)、非易失性存储
实践提示:在单体应用中,ACID由数据库引擎直接保障。开发者只需声明事务边界(BEGIN/COMMIT/ROLLBACK),无需关心底层实现。这是单体架构在数据一致性方面的最大红利。

3.2 本地事务与全局事务

根据事务涉及的数据源范围,传统事务模型分为本地事务和全局事务两类。理解两者的区别,是后续向分布式事务演进的前提。

3.2.1 本地事务(Local Transaction)

本地事务是指仅操作单一数据库资源的事务。它由数据库自身的事务管理器(如MySQL的InnoDB引擎、Oracle的Undo表空间)直接管理,不涉及外部协调者。

  • 典型场景:单体应用连接单个MySQL实例,执行订单插入+库存扣减+账户扣款(三张表在同一数据库)。
  • 实现方式:通过JDBC的Connection.setAutoCommit(false) + commit() / rollback()。
  • 优势:性能高(无网络开销)、实现简单、ACID完全由数据库保证。
  • 局限:无法跨数据库、跨消息队列、跨HTTP服务。
// 本地事务示例(Java JDBC)
Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false);
    
    // 操作1:插入订单
    orderDao.insert(order);
    // 操作2:扣减库存
    inventoryDao.decrease(productId, quantity);
    // 操作3:扣减账户余额
    accountDao.deduct(userId, amount);
    
    conn.commit();  // 全部成功,提交
} catch (Exception e) {
    conn.rollback();  // 任一失败,全部回滚
} finally {
    conn.close();
}

3.2.2 全局事务(Global Transaction / XA Transaction)

全局事务是指跨多个资源管理器(如多个数据库、消息队列、文件系统)的事务。它需要引入一个外部协调者——事务管理器(Transaction Manager),通过两阶段提交(2PC)协议保证所有参与者的原子性。

  • 典型场景:单体应用同时操作MySQL数据库和RabbitMQ消息队列,要求两者保持最终一致。
  • 实现方式:JTA(Java Transaction API)+ XA协议,或.NET的System.Transactions。
  • 优势:能够跨异构资源保证ACID。
  • 局限:性能差(同步阻塞、锁持有时间长)、实现复杂、对资源管理器有XA兼容性要求、存在协调者单点故障风险。
架构避坑:全局事务(XA/2PC)在单体架构中已显笨重,在微服务架构中更是灾难。后续章节将看到,分布式系统通常放弃全局事务,转而采用最终一致性+补偿机制。

3.3 在事件风暴画布中建模单体事务

事件风暴(Event Storming)是一种协作建模技术,通过“事件-命令-聚合-策略”等元素可视化业务流。在单体事务场景中,我们关注的是:如何在一个事务边界内,将多个领域事件和命令映射到同一个数据库事务中。

3.3.1 建模步骤

  1. 识别领域事件(Domain Event):橙色便签,记录业务中发生的事实(如“订单已创建”“库存已扣减”“账户已扣款”)。
  2. 识别命令(Command):蓝色便签,触发事件的用户意图或系统动作(如“提交订单”“扣减库存”“扣款”)。
  3. 识别聚合(Aggregate):黄色便签,命令执行的目标实体(如“订单”“库存”“账户”)。
  4. 划定事务边界(Transaction Boundary):用红色虚线框将必须在一个ACID事务中完成的所有操作圈在一起。
  5. 标注策略(Policy):紫色便签,描述事务失败时的补偿或重试规则(单体事务中通常为“回滚并抛出异常”)。

3.3.2 单体事务建模示例

以下SVG图展示了一个典型的“下单扣库存扣款”单体事务在事件风暴画布中的建模结果。所有操作被红色虚线框包围,表示它们共享同一个数据库事务。

事件风暴画布:单体事务建模(下单扣库存扣款) 事务边界(ACID) 命令:提交订单 用户点击“提交”按钮 聚合:订单 Order Aggregate 事件:订单已创建 OrderCreated 命令:扣减库存 系统自动触发 聚合:库存 Inventory Aggregate 事件:库存已扣减 InventoryDecreased 命令:扣款 系统自动触发 聚合:账户 Account Aggregate 事件:账户已扣款 AccountDebited 策略:任一失败 → 全部回滚 Policy: Rollback All on Failure 说明:红色虚线框内所有操作在同一个数据库连接中执行,由数据库事务管理器保证ACID。

3.3.3 单体事务建模要点

  • 事务边界必须包含所有写操作:任何对数据库状态的修改(INSERT/UPDATE/DELETE)都必须落在边界内。
  • 读操作可以游离在边界外:查询类命令不需要事务保护,除非要求“可重复读”隔离级别。
  • 避免跨边界调用:如果某个命令需要调用外部HTTP服务或消息队列,则无法使用单体事务——这是后续分布式事务的切入点。
  • 策略(Policy)通常为“回滚”:在单体事务中,失败策略几乎总是“全部回滚并抛出异常”,由上层调用方决定是否重试。

3.4 单体事务的适用边界与演进信号

单体事务并非万能。以下表格帮助判断何时应该坚持单体事务,何时应该考虑向分布式事务演进。

判断维度 适合单体事务 需要演进(分布式事务)
数据源数量 单一数据库 多个数据库、消息队列、缓存、文件系统
服务架构 单体应用(Monolith) 微服务、服务网格、无服务器架构
一致性要求 强一致性(ACID) 最终一致性(BASE)
事务持续时间 毫秒级(短事务) 秒级或更长(长事务/柔性事务)
性能敏感度 高(本地事务开销极低) 可接受一定延迟(2PC、Saga、TCC)
团队规模 小团队(< 10人) 多团队协作(> 10人)
关键认知:在事件风暴画布中,如果某个事务边界内出现了“发送邮件”“调用第三方API”“写入消息队列”等操作,说明单体事务已经无法满足需求。此时,要么将边界缩小(只保护数据库操作),要么引入分布式事务模式(如Saga、TCC、事件驱动)。这正是本课程后续章节要深入探讨的内容。