2.5 Java帝国之宫廷内斗

把跨资源事务拆成 JDBC 参与者、JTA 协调、prepare 投票、全局决定和恢复,理解原子性与可用性的代价。

学习目标

  • 能沿 JTA 事务、协调者、JDBC 参与者和两个资源追踪 prepare/commit/rollback 状态
  • 能解释两阶段提交如何换取全局原子决定,以及为什么会引入锁、阻塞和恢复日志
  • 能用正常、边界和单一故障样本定位投票拒绝、协调者失联、重试重复和基本可用降级

2.5 Java帝国之宫廷内斗

本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 2.5 Java帝国之宫廷内斗。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。

跨两个数据库、消息资源或其他可参与资源写入时,各自本地提交不足以证明全局原子性。JTA 为应用提供全局事务边界,协调者先让每个 JDBC 参与者执行并准备,再根据所有投票广播 commit 或 rollback。它减少“半边成功”的概率,却要求参与者在不确定时保留锁和 prepared 状态,因而增加阻塞、超时和恢复复杂度。

两阶段提交:原子决定换来等待与恢复成本prepare 不是成功;决定未知时,参与者必须保留足够证据1JTA全局事务协议证据2协调者记录决定协议证据3prepare投票与持锁第一阶段4commit/rollback统一广播恢复证据5恢复日志与释放恢复证据协调者失联后只能保留 in-doubt,不应凭超时猜测 commit
专属图示:把 JTA、参与者投票、全局决定和恢复成本放回同一协议链。

三个会让全局事务失真的陷阱

十个目录节点到协议证据

JDBC大臣

不是一个会自动服从命令的对象。参与者必须能保存 prepare 状态、响应 commit/rollback、报告失败,并把本地日志和锁纳入恢复。连接池、自动提交和隔离级别会影响它能否诚实投票。

密谋

可理解为 prepare 阶段的内部协商。参与者要检查约束、锁和日志;返回 yes 代表“我可以按协议提交”,不是“业务已经完成”。任何一个 no 都足以让协调者选择回滚。

两阶段提交

的第一阶段保证协调者知道谁能提交,第二阶段传播唯一决定。它不能消灭协调者、网络或参与者故障;prepare 后失联会留下 in-doubt 与阻塞窗口,因此必须有超时、日志和恢复协议。

JTA

把应用和具体事务管理器解耦,但事务管理器仍需知道资源、超时、传播和恢复。JTA 的提交结果不覆盖未登记的外部服务,也不自动让本地非事务操作具备原子性。

塞翁失马,焉知非福

不是为故障找乐观解释,而是提醒保留失败轨迹:某次超时也许揭示锁没有释放,某次回滚也许揭示事件发送早于提交。修复要让同一故障重放时得到更清晰的拒绝或恢复。

基本可用

不是无条件放行。设计时先声明业务可接受的降级:读旧快照、排队等待、拒绝写入或进入补偿;每种选择都要标注可能的陈旧、重复或延迟,并能在恢复后对账。

走漏风声

提示观察通知顺序。提交事件应在最终决定可确认后发送,或携带幂等键与可追踪状态;prepare、锁等待和本地中间值不应冒充业务成功。

宫廷激辩

要求把争论变成场景表:哪些资源必须原子,哪些可以最终一致,协调者失联时是否阻塞,业务能接受多长恢复时间。没有这些输入,“选择 2PC”或“拒绝 2PC”都只是口号。

2.5 Java帝国之宫廷内斗

这场“内斗”的工程事实是多个事务参与者共享一个全局决定,却各自拥有本地锁、日志和失败模式。2.5 Java帝国之宫廷内斗不是在赞美或否定 JTA,而是训练读者沿 prepare、决定、释放和恢复验证原子性,以及诚实记录可用性代价。

最小两阶段提交合同

enum Vote { YES, NO }
enum Decision { COMMIT, ROLLBACK, IN_DOUBT }
 
record Participant(String id, Vote vote, boolean prepared, boolean released) {}
 
static Decision decide(List<Participant> participants, boolean coordinatorAlive) {
  if (!coordinatorAlive) return Decision.IN_DOUBT;
  return participants.stream().allMatch(p -> p.vote() == Vote.YES && p.prepared())
      ? Decision.COMMIT
      : Decision.ROLLBACK;
}

合同把 prepared、最终 Decisionreleased 分开。协调者存活且所有参与者都准备好,才可广播 commit;任何 no 选择 rollback;协调者失联时只能记录 in-doubt,不能凭超时猜测成功。外部副作用还需要独立的幂等或补偿设计。

四步复核一个全局事务

分步1 / 4

1. 开启 JTA 事务并登记参与者

列出事务边界、JDBC 连接、资源身份、超时和传播方式。正常样本登记两个参与者;边界样本缺少一个资源或混入未登记副作用,观察合同如何拒绝。

两阶段提交:原子决定换来等待与恢复成本prepare 不是成功;决定未知时,参与者必须保留足够证据1JTA全局事务协议证据2协调者记录决定协议证据3prepare投票与持锁第一阶段4commit/rollback统一广播恢复证据5恢复日志与释放恢复证据协调者失联后只能保留 in-doubt,不应凭超时猜测 commit
专属图示:把 JTA、参与者投票、全局决定和恢复成本放回同一协议链。

Lab

两阶段提交决策台

只改变一个参与者或协调者条件,观察 commit、rollback 和 in-doubt 的差别。

两个参与者都 prepare=yes

A yes + B yes → decision commit → acknowledgements → locks released

判定

commit:广播后幂等确认

当前样本:全票提交;保存全局事务 ID、投票、决定、锁和恢复日志。

正常、边界与单一故障证据

两阶段证据矩阵:一致决定还要能恢复正常样本看决定,边界样本看等待,故障样本看未知状态观察项正常边界故障参与者都投 YES一个 NO状态未知协调者决定持久广播重试准备后失联决定后释放等待超时长期阻塞恢复重放完成幂等确认日志缺口先找协调决定和恢复日志,再释放锁或选择基本可用策略
专属图示:从投票到恢复逐行验证两阶段提交的代价。
样本只改变的变量预期判定必存证据
正常所有参与者 prepare=yes,协调者可达统一 commit 后释放资源投票、决定日志、确认、锁释放
边界一个参与者 no、超时或重复决定全局 rollback 或幂等确认参与者状态、超时、重试与版本
单一故障prepare 后协调者失联in-doubt 可恢复,不猜测 commit日志、锁、恢复来源、降级策略

故障诊断:先确认决定是否存在

  1. 登记层:确认 JTA 事务是否包含所有需要原子的资源,外部邮件/HTTP/缓存是否另有合同。
  2. 投票层:查看每个参与者的本地约束、日志、锁和 prepare 结果;一个 no 不应被转换为 yes。
  3. 决定层:查协调者的持久化决定和广播确认;区分 rollback、commit 与 in-doubt,不能以连接超时推断结果。
  4. 恢复层:根据日志恢复参与者,释放锁并执行幂等补偿;基本可用策略必须保留一致性和陈旧性说明。

如果数据库都已提交但消息未发出,检查 outbox;如果一个参与者持锁等待,先查协调者决定日志;如果重复提交,检查决定幂等和客户端重试键。每个故障都要能从唯一业务身份重放。

最小全局事务证据包与反例

证据包包含全局事务 ID、JTA 传播、参与者列表、连接/资源身份、SQL、投票、prepare 日志、协调决定、广播确认、锁、超时、重试、外部副作用和恢复结果。敏感数据可脱敏,但不能删除决定链和参与者身份。

反例一是参与者全部 prepare 后协调者失联,系统为了“基本可用”直接放行同一资源的下一次写入;反例二是数据库回滚但邮件已发送。保留未知状态,再用决定日志、outbox 或补偿重放,证明修复没有把未知变成猜测。

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

JDBC大臣

登记在全局事务中的 JDBC 参与者,负责本地 SQL、prepare、决定确认和资源释放。

密谋

参与者检查本地条件并写预备日志、返回投票的 prepare 阶段。

两阶段提交

先收集 prepare 投票,再广播统一 commit 或 rollback 的协议。

JTA

Java 应用声明全局事务、登记资源并交由事务管理器协调的抽象。

塞翁失马,焉知非福

将失败样本转化为协议、恢复和可重放证据的复盘视角。

基本可用

在决定未知或资源不可达时明确允许的只读、排队、隔离或补偿策略。

走漏风声

未提交状态或中间决定过早泄露,被下游误当成最终业务事实。

宫廷激辩

围绕原子性、可用性、延迟、锁和补偿做场景化工程取舍。

练习

练习

问题 1: 两个参与者都返回 prepare=yes,但协调者在广播决定前宕机。系统为什么不能直接当作已提交?

问题 2: JTA 事务回滚了数据库,但邮件已经发出。JTA 哪里没有覆盖?

问题 3: 为了保持基本可用,协调者失联时允许读写同一订单。怎样证明这不是静默破坏一致性?

资料与写作方式声明

本章以码农翻身权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

本页小结

2.5 Java帝国之宫廷内斗的关键不是把 JTA 当成魔法,而是能沿参与者投票、协调决定、锁、日志和恢复区分 commit、rollback 与 in-doubt。完成标准是知道两阶段提交换来的原子性代价,并为基本可用和未登记副作用设计明确的降级、幂等或补偿合同。

讨论

评论区加载中…