事务选型指南
用多数据源的人一定会撞上事务问题。这一章不讲「有哪些方案」,而是帮你在 30 秒内选对方案。
先理解一个事实
Spring 原生事务一旦开启,整个线程后续拿到的都是同一个
Connection。
数据源切换靠的是 ThreadLocal 里的一个 key,而 @Transactional 会把连接绑定到线程上。两者叠加的结果是:事务开启之后,切换注解和手动 push 都不再起作用。
这不是 bug,而是关系型数据库事务模型与「一次调用一个连接」的必然结果。理解了这一点,下面所有方案都好选了。
决策树
需要事务吗?
└─ 不需要 → 什么都不用做
需要。事务内会切换到别的库吗?
├─ 不会(只有单库读写)→ 用 Spring 原生 @Transactional(配合 @DS)
│
└─ 会
├─ 同一个应用进程内 → 用 @DSTransactional
└─ 跨服务/跨进程 → 用 Seata(AT 或 XA)
方案对比
| 方案 | 适用场景 | 外部依赖 | 跨服务 | 一致性 |
|---|---|---|---|---|
@Transactional + @DS | 事务内不切库的单库事务 | 无 | 否 | 强一致(数据库保证) |
@DSTransactional | 单体应用内跨多个库 | 无 | 否 | 尽力而为(见下方说明) |
Seata AT | 微服务跨服务 | Seata Server | 是 | 最终一致,自动补偿 |
Seata XA | 跨服务且要求强一致 | Seata Server | 是 | 强一致,性能较低 |
| Atomikos XA | 单进程内需要标准 XA | Atomikos | 否 | 强一致 |
@DSTransactional 不是严格的 2PC@DSTransactional 的做法是:为涉及的每个库各开一个本地事务,全部挂在同一个 XID 下,业务方法正常返回时逐库提交,抛异常时逐库回滚。
回滚是尽力而为的——某个库回滚失败不会阻止其他库回滚。提交同样是逐个进行的——如果某个库在 commit 阶段失败,先前已经提交的库无法再回滚。
所以它适合「单体应用内、跨库一致性要求不是 100% 苛刻」的场景。金融级强一致请上 XA。
本章内容
- 为什么事务下切换不了数据源 —— 原理,以及三个高频误区的正解
- 事务常见问题 —— 混用陷阱、
@DS与@Transactional的共存规则 - 本地多数据源事务 ——
@DSTransactional完整用法、7 种传播行为、@DsTxEventListener - Seata 事务 —— 完整可运行示例
- 事务概念 —— ACID、JDBC 事务与 Spring 事务的科普