<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Distributed-Systems on Orangex-position0的技术博客</title><link>https://Orangex-position0.github.io/categories/distributed-systems/</link><description>Recent content in Distributed-Systems on Orangex-position0的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 26 May 2026 19:10:00 +0800</lastBuildDate><atom:link href="https://Orangex-position0.github.io/categories/distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>使用 TCC 模式处理分布式事务一致性</title><link>https://Orangex-position0.github.io/posts/distributed-systems/tcc-distributed-transaction/</link><pubDate>Tue, 26 May 2026 19:10:00 +0800</pubDate><guid>https://Orangex-position0.github.io/posts/distributed-systems/tcc-distributed-transaction/</guid><description>&lt;h2 id="背景--问题"&gt;&lt;a href="#%e8%83%8c%e6%99%af--%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;背景 &amp;amp; 问题
&lt;/h2&gt;&lt;p&gt;在分布式系统中，一个业务操作往往涉及多个服务。比如电商下单：库存服务扣减库存、订单服务创建订单、支付服务冻结金额。这些操作要么全部成功，要么全部失败——这就是&lt;strong&gt;分布式事务&lt;/strong&gt;问题。&lt;/p&gt;
&lt;p&gt;传统的单体应用可以用数据库事务（ACID）轻松解决，但分布式环境下，每个服务有自己的数据库，无法共享一个事务。常见的分布式事务方案有：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;方案&lt;/th&gt;
					&lt;th&gt;核心思路&lt;/th&gt;
					&lt;th&gt;主要问题&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;2PC（两阶段提交）&lt;/td&gt;
					&lt;td&gt;协调者统一控制所有参与者的提交/回滚&lt;/td&gt;
					&lt;td&gt;同步阻塞、单点故障、性能差&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;3PC（三阶段提交）&lt;/td&gt;
					&lt;td&gt;在 2PC 基础上增加&amp;quot;预提交&amp;quot;阶段降低阻塞&lt;/td&gt;
					&lt;td&gt;实现复杂，仍存在网络分区问题&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些方案依赖数据库层面的全局锁，在高并发场景下性能瓶颈明显。&lt;/p&gt;
&lt;p&gt;本文将介绍 TCC（Try-Confirm-Cancel）模式——一种参考 2PC 思想、但不依赖全局锁的分布式事务方案。你将了解到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCC 的核心思想和三阶段工作流程&lt;/li&gt;
&lt;li&gt;TCC 架构设计及其与 2PC 的对比&lt;/li&gt;
&lt;li&gt;用资金转账场景演示 TCC 的完整实现&lt;/li&gt;
&lt;li&gt;6 个实现中必须解决的关键问题及其解决方案&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="前置知识"&gt;&lt;a href="#%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86" class="header-anchor"&gt;&lt;/a&gt;前置知识
&lt;/h3&gt;&lt;p&gt;阅读本文需要了解以下概念（不熟悉也没关系，文中会配合上下文说明）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分布式事务&lt;/strong&gt;：跨多个服务或数据库的事务操作，需要保证数据一致性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终一致性&lt;/strong&gt;：允许数据在短时间内不一致，但保证最终会达到一致状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;幂等性&lt;/strong&gt;：同一操作执行一次和多次产生的效果相同&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="方案或原理"&gt;&lt;a href="#%e6%96%b9%e6%a1%88%e6%88%96%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;方案或原理
&lt;/h2&gt;&lt;h3 id="tcc-是什么"&gt;&lt;a href="#tcc-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;TCC 是什么
&lt;/h3&gt;&lt;p&gt;TCC（Try-Confirm-Cancel）是一种基于&lt;strong&gt;业务补偿&lt;/strong&gt;的分布式事务解决方案。它将一个完整的业务操作拆分为三个阶段：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;阶段&lt;/th&gt;
					&lt;th&gt;职责&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Try&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;资源预留 / 可行性检查&lt;/td&gt;
					&lt;td&gt;冻结资源、检查前置条件，但不真正执行业务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Confirm&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;确认提交&lt;/td&gt;
					&lt;td&gt;真正执行业务操作，使用 Try 阶段预留的资源&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Cancel&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;取消回滚&lt;/td&gt;
					&lt;td&gt;释放 Try 阶段预留的资源，恢复到操作前状态&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;核心思想：&lt;strong&gt;不依赖数据库全局锁，而是通过&amp;quot;资源预留 + 显式确认/取消&amp;quot;来控制事务状态&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;TCC 本质上是一个&lt;strong&gt;带重试、可回滚的分布式状态机&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="tcc-vs-2pc"&gt;&lt;a href="#tcc-vs-2pc" class="header-anchor"&gt;&lt;/a&gt;TCC vs 2PC
&lt;/h3&gt;&lt;p&gt;两者都采用&amp;quot;两阶段&amp;quot;的思想，但有本质区别：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;2PC&lt;/th&gt;
					&lt;th&gt;TCC&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;锁机制&lt;/td&gt;
					&lt;td&gt;数据库全局锁&lt;/td&gt;
					&lt;td&gt;业务层面资源预留（无全局锁）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;实现层面&lt;/td&gt;
					&lt;td&gt;数据库 / 中间件&lt;/td&gt;
					&lt;td&gt;业务代码（每个服务实现 Try/Confirm/Cancel）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;性能&lt;/td&gt;
					&lt;td&gt;同步阻塞，资源锁定时间长&lt;/td&gt;
					&lt;td&gt;无全局锁，并发性能好&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;侵入性&lt;/td&gt;
					&lt;td&gt;对业务代码侵入低&lt;/td&gt;
					&lt;td&gt;需要业务代码配合实现三个阶段&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;适用场景&lt;/td&gt;
					&lt;td&gt;对一致性要求极高&lt;/td&gt;
					&lt;td&gt;高并发、短事务场景&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;简单来说：2PC 让数据库来管锁，TCC 让业务代码自己管资源。&lt;/p&gt;
&lt;h3 id="tcc-架构"&gt;&lt;a href="#tcc-%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;TCC 架构
&lt;/h3&gt;&lt;p&gt;&lt;img alt="TCC Architecture" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://Orangex-position0.github.io/posts/distributed-systems/tcc-distributed-transaction/tcc-architecture.svg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心组件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;事务管理器（TM）&lt;/strong&gt;：发起分布式事务，决定全局提交或回滚&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事务协调器（TC）&lt;/strong&gt;：协调各分支事务的 Try/Confirm/Cancel 执行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参与者（RM）&lt;/strong&gt;：各业务服务，实现 Try/Confirm/Cancel 三个接口&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tcc-通用流程"&gt;&lt;a href="#tcc-%e9%80%9a%e7%94%a8%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;TCC 通用流程
&lt;/h3&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TD
 A[发起分布式事务] --&gt; B[Try 阶段: 资源预留/检查]
 B --&gt; C{所有Try执行成功?}

 C -- 全部成功 --&gt; D[Confirm 阶段: 真正提交执行]
 D --&gt; E[事务完成]

 C -- 部分失败 --&gt; F[Cancel 阶段: 回滚/释放资源]
 F --&gt; G[事务回滚完成]&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;事务管理器发起分布式事务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Try 阶段&lt;/strong&gt;：所有参与者执行资源预留和可行性检查&lt;/li&gt;
&lt;li&gt;如果所有 Try 都成功，进入 &lt;strong&gt;Confirm 阶段&lt;/strong&gt;，提交业务操作&lt;/li&gt;
&lt;li&gt;如果任何一个 Try 失败，进入 &lt;strong&gt;Cancel 阶段&lt;/strong&gt;，释放资源并回滚&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="优缺点"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;优缺点
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能高：避免数据库长事务与全局锁&lt;/li&gt;
&lt;li&gt;隔离性好：资源预留后对其他事务不可见&lt;/li&gt;
&lt;li&gt;保证最终一致性，支持回滚操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;代价：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现复杂：每个业务需要实现三个接口&lt;/li&gt;
&lt;li&gt;对幂等要求极高：网络重试可能导致重复调用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="适用场景"&gt;&lt;a href="#%e9%80%82%e7%94%a8%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;适用场景
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高并发、短事务&lt;/strong&gt;：如电商下单、支付冻结&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对一致性要求高&lt;/strong&gt;：如金融交易&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不适合长事务&lt;/strong&gt;：资源会被长期冻结，影响系统性能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实现步骤"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e6%ad%a5%e9%aa%a4" class="header-anchor"&gt;&lt;/a&gt;实现步骤
&lt;/h2&gt;&lt;p&gt;下面通过一个&lt;strong&gt;资金转账&lt;/strong&gt;场景（A 账户向 B 账户转账 100 元）演示 TCC 的完整实现。&lt;/p&gt;
&lt;h3 id="场景分析"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e5%88%86%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;场景分析
&lt;/h3&gt;&lt;p&gt;涉及两个服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;转出服务&lt;/strong&gt;：从 A 账户扣款&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;转入服务&lt;/strong&gt;：向 B 账户加款&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="1-try-阶段资源预留"&gt;&lt;a href="#1-try-%e9%98%b6%e6%ae%b5%e8%b5%84%e6%ba%90%e9%a2%84%e7%95%99" class="header-anchor"&gt;&lt;/a&gt;1. Try 阶段：资源预留
&lt;/h3&gt;&lt;p&gt;Try 阶段不是真正扣款/加款，而是&amp;quot;冻结&amp;quot;对应金额。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转出服务 - Try：冻结 A 账户 100 元&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tryDeduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 检查余额是否充足&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InsufficientBalanceException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;余额不足&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 冻结金额（可用余额减少，冻结金额增加）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 记录事务日志（幂等控制）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TRY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转入服务 - Try：预增加 B 账户 100 元（标记为待确认）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tryAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 预增加金额（标记为冻结状态，不可用）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;preAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TRY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键点：Try 阶段通过&amp;quot;冻结&amp;quot;实现资源预留，而非真正执行业务。&lt;/p&gt;
&lt;h3 id="2-confirm-阶段确认提交"&gt;&lt;a href="#2-confirm-%e9%98%b6%e6%ae%b5%e7%a1%ae%e8%ae%a4%e6%8f%90%e4%ba%a4" class="header-anchor"&gt;&lt;/a&gt;2. Confirm 阶段：确认提交
&lt;/h3&gt;&lt;p&gt;所有 Try 成功后，执行真正的业务操作。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转出服务 - Confirm：真正扣除冻结金额&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;confirmDeduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 幂等检查：是否已执行过&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CONFIRM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 扣除冻结金额&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deductFrozen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CONFIRM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转入服务 - Confirm：将预增加金额转为可用&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;confirmAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CONFIRM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;confirmPreAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CONFIRM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-cancel-阶段回滚释放"&gt;&lt;a href="#3-cancel-%e9%98%b6%e6%ae%b5%e5%9b%9e%e6%bb%9a%e9%87%8a%e6%94%be" class="header-anchor"&gt;&lt;/a&gt;3. Cancel 阶段：回滚释放
&lt;/h3&gt;&lt;p&gt;任何一个 Try 失败时，所有参与者执行 Cancel 释放资源。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转出服务 - Cancel：解冻金额&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancelDeduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 空回滚检查：Try 是否执行过&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TRY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 解冻金额&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unfreeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 转入服务 - Cancel：撤销预增加&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancelAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TRY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cancelPreAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意每个 Cancel 方法都包含了&lt;strong&gt;幂等检查&lt;/strong&gt;和&lt;strong&gt;空回滚检查&lt;/strong&gt;——这是 TCC 实现中最关键的两个防护机制。&lt;/p&gt;
&lt;h2 id="示例--踩坑"&gt;&lt;a href="#%e7%a4%ba%e4%be%8b--%e8%b8%a9%e5%9d%91" class="header-anchor"&gt;&lt;/a&gt;示例 / 踩坑
&lt;/h2&gt;&lt;p&gt;TCC 的核心难点不在&amp;quot;正常流程&amp;quot;，而在各种异常场景下的正确处理。以下是 6 个实现中必须解决的问题。&lt;/p&gt;
&lt;h3 id="1-幂等问题"&gt;&lt;a href="#1-%e5%b9%82%e7%ad%89%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;1. 幂等问题
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：网络超时后协调器会重试 Confirm/Cancel，可能导致同一操作被执行多次。例如：已经扣款的账户被再次扣款。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;唯一事务 ID&lt;/strong&gt;：每次操作携带全局唯一的事务 ID&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事务状态日志&lt;/strong&gt;：执行前检查该事务 ID 是否已执行过当前阶段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库防重&lt;/strong&gt;：通过唯一索引防止重复提交&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个 Confirm/Cancel 方法的开头都要做幂等检查：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CONFIRM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-空回滚"&gt;&lt;a href="#2-%e7%a9%ba%e5%9b%9e%e6%bb%9a" class="header-anchor"&gt;&lt;/a&gt;2. 空回滚
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：Try 请求因网络丢包未到达参与者，但协调器因超时触发 Cancel。此时参与者的 Cancel 没有对应的 Try 操作，执行回滚逻辑会导致数据错误。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cancel 执行前检查 Try 是否已执行（查询事务日志）&lt;/li&gt;
&lt;li&gt;如果 Try 未执行，直接记录日志并返回成功，不执行业务逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 空回滚检查&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TRY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionLogDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CANCEL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-悬挂事务"&gt;&lt;a href="#3-%e6%82%ac%e6%8c%82%e4%ba%8b%e5%8a%a1" class="header-anchor"&gt;&lt;/a&gt;3. 悬挂事务
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：Try 请求网络延迟，Cancel 已经先于 Try 到达并完成（触发了空回滚）。随后延迟的 Try 才到达并执行成功，资源被冻结但无人推进事务——形成&amp;quot;悬挂&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定时轮询监控&lt;/strong&gt;：后台任务周期扫描，识别长时间停留在 Try 阶段的事务并主动推进&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MQ 异步补偿&lt;/strong&gt;：通过消息队列异步触发后续 Confirm/Cancel 操作，解耦并保障执行可靠性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-补偿失败"&gt;&lt;a href="#4-%e8%a1%a5%e5%81%bf%e5%a4%b1%e8%b4%a5" class="header-anchor"&gt;&lt;/a&gt;4. 补偿失败
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：自动补偿机制可能失效（如数据库宕机、代码 Bug），需要人工介入保证数据一致性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;预留补偿接口&lt;/strong&gt;：为 Try/Confirm/Cancel 分别提供可手动触发的补偿接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事务状态查询接口&lt;/strong&gt;：供运维人员查询事务当前状态，判断是否需要补偿&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;补偿任务调度&lt;/strong&gt;：定时扫描未完成的事务分支，自动重试补偿&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-超时处理"&gt;&lt;a href="#5-%e8%b6%85%e6%97%b6%e5%a4%84%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;5. 超时处理
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：受网络分区、服务宕机等影响，事务长期滞留于 Try 阶段，无法正常推进。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：设置事务超时时间，超时后自动标记为失败，触发 Cancel 回滚。&lt;/p&gt;
&lt;h3 id="6-协调者故障"&gt;&lt;a href="#6-%e5%8d%8f%e8%b0%83%e8%80%85%e6%95%85%e9%9a%9c" class="header-anchor"&gt;&lt;/a&gt;6. 协调者故障
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：事务协调器是 TCC 的核心组件，一旦故障，所有进行中的事务都无法推进。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;协调器集群部署，主节点故障后备用节点接管&lt;/li&gt;
&lt;li&gt;提供管理后台，允许运维人员手动决定提交或回滚&lt;/li&gt;
&lt;li&gt;持久化事务日志，恢复后可继续推进未完成的事务&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="常见-tcc-组件"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81-tcc-%e7%bb%84%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;常见 TCC 组件
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;组件&lt;/th&gt;
					&lt;th&gt;语言&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a class="link" href="https://seata.apache.org/" target="_blank" rel="noopener"
 &gt;Apache Seata&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Java&lt;/td&gt;
					&lt;td&gt;功能全面，支持 AT/TCC/Saga/XA 四种模式&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a class="link" href="https://entiy.dtm.pub/" target="_blank" rel="noopener"
 &gt;DTM&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Go&lt;/td&gt;
					&lt;td&gt;支持多语言 SDK，轻量级&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a class="link" href="https://github.com/Orangex-position0/rust-tcc" target="_blank" rel="noopener"
 &gt;rust-tcc&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Rust&lt;/td&gt;
					&lt;td&gt;轻量级 TCC 实现&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;本文介绍了 TCC 分布式事务模式的核心思想和实践方法，回顾要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;核心思路&lt;/strong&gt;：将事务拆分为 Try（资源预留）、Confirm（确认提交）、Cancel（取消回滚）三个阶段，通过业务补偿代替全局锁&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与 2PC 的区别&lt;/strong&gt;：TCC 在业务层面控制资源而非依赖数据库锁，性能更好但实现更复杂&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键实现挑战&lt;/strong&gt;：幂等性、空回滚、悬挂事务、补偿失败、超时处理、协调者故障——每个都需要针对性的解决方案&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：高并发短事务（电商下单、支付冻结），不适合长事务&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCC 不是银弹。如果你的系统事务简单、并发不高，可以考虑更轻量的方案（如本地消息表）。当系统确实面临&amp;quot;分布式事务 + 高并发&amp;quot;的瓶颈时，TCC 是一个值得投入的解决方案。&lt;/p&gt;</description></item><item><title>使用 CQRS 处理读写分离架构</title><link>https://Orangex-position0.github.io/posts/distributed-systems/cqrs-architecture/</link><pubDate>Tue, 05 May 2026 10:00:00 +0800</pubDate><guid>https://Orangex-position0.github.io/posts/distributed-systems/cqrs-architecture/</guid><description>&lt;h2 id="背景--问题"&gt;&lt;a href="#%e8%83%8c%e6%99%af--%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;背景 &amp;amp; 问题
&lt;/h2&gt;&lt;p&gt;在传统架构中，单个数据模型通常兼顾读和写的职责。但随着系统规模扩展，读和写操作在性能和扩展性上呈现出明显的不对称性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据表示不匹配&lt;/strong&gt;：写入时需要的字段结构与读取时需要的展示形式往往不同。例如，创建订单需要商品 ID 列表，但查询订单详情时用户希望看到商品名称和价格&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锁竞争严重&lt;/strong&gt;：读操作和写操作共享同一份数据，复杂查询（join / 聚合 / 过滤）会长时间占用连接，阻塞写操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能无法兼顾&lt;/strong&gt;：写入追求强一致性和事务安全，读取追求高并发和低延迟，两者的优化方向天然矛盾&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些问题的本质是：&lt;strong&gt;单一数据模型无法同时高效满足读与写需求&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本文将介绍 CQRS（Command Query Responsibility Segregation）模式，说明它如何通过读写职责分离来解决上述问题，并给出从简单到复杂的三种演进方案和完整的电商订单系统案例。&lt;/p&gt;
&lt;h3 id="前置知识"&gt;&lt;a href="#%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86" class="header-anchor"&gt;&lt;/a&gt;前置知识
&lt;/h3&gt;&lt;p&gt;阅读本文需要了解以下概念（不熟悉也没关系，文中会配合上下文说明）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;领域驱动设计（DDD）&lt;/strong&gt;：一种以业务领域为核心组织代码的方法论，强调领域模型和业务规则的封装&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;领域模型（Domain Model）&lt;/strong&gt;：面向业务的对象模型，包含实体、值对象和聚合根，封装业务规则&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;领域事件（Domain Event）&lt;/strong&gt;：表示领域中发生的重要事情（如&amp;quot;订单已创建&amp;quot;），用于解耦不同模块之间的通信&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合根（Aggregate Root）&lt;/strong&gt;：一组相关对象的入口点，保证业务规则的一致性边界&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="方案或原理"&gt;&lt;a href="#%e6%96%b9%e6%a1%88%e6%88%96%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;方案或原理
&lt;/h2&gt;&lt;h3 id="cqrs-是什么"&gt;&lt;a href="#cqrs-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;CQRS 是什么
&lt;/h3&gt;&lt;p&gt;CQRS（Command Query Responsibility Segregation）是一种架构模式，核心思想是：&lt;strong&gt;将修改数据的操作（Command）和读取数据的操作（Query）彻底解耦&lt;/strong&gt;，使用不同的模型、流程和存储来分别服务。&lt;/p&gt;
&lt;p&gt;简单来说，就是让&amp;quot;写&amp;quot;和&amp;quot;读&amp;quot;各走各的路，各自优化。&lt;/p&gt;
&lt;h3 id="优缺点"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;优缺点
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;写操作聚焦强一致性和业务规则，读操作聚焦高并发和数据展示&lt;/li&gt;
&lt;li&gt;读写可独立扩容，读模型可以使用缓存、搜索引擎等专门优化&lt;/li&gt;
&lt;li&gt;降低单个模型的复杂度，职责更清晰&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;代价：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要维护两套模型，开发成本和学习成本增加&lt;/li&gt;
&lt;li&gt;读模型通常异步更新，存在数据延迟（最终一致性）&lt;/li&gt;
&lt;li&gt;事件同步、消息队列等基础设施需要额外运维&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="适用场景"&gt;&lt;a href="#%e9%80%82%e7%94%a8%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;适用场景
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;场景&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;读多写少&lt;/td&gt;
					&lt;td&gt;用户频繁查询但写入较少，如电商订单查询&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;查询逻辑复杂&lt;/td&gt;
					&lt;td&gt;需要多表 join、聚合计算，如报表系统&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;读写性能要求差异大&lt;/td&gt;
					&lt;td&gt;写要求强一致，读要求低延迟高并发&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;业务逻辑复杂&lt;/td&gt;
					&lt;td&gt;领域规则复杂，不适合在查询中混入业务逻辑&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;如果你的系统读写都很简单，或者读远大于写但查询也不复杂，引入 CQRS 可能是过度设计。先评估实际需求，再决定是否采用。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="五个设计原则"&gt;&lt;a href="#%e4%ba%94%e4%b8%aa%e8%ae%be%e8%ae%a1%e5%8e%9f%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;五个设计原则
&lt;/h3&gt;&lt;h4 id="1-读写职责分离"&gt;&lt;a href="#1-%e8%af%bb%e5%86%99%e8%81%8c%e8%b4%a3%e5%88%86%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;1. 读写职责分离
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;写操作只负责&lt;strong&gt;修改状态 + 执行业务规则&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;读操作只负责&lt;strong&gt;数据获取 + 展示优化&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;禁止在 Query 中修改状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目的：避免读路径被业务逻辑污染，也保证查询可独立优化。&lt;/p&gt;
&lt;h4 id="2-一致性策略明确"&gt;&lt;a href="#2-%e4%b8%80%e8%87%b4%e6%80%a7%e7%ad%96%e7%95%a5%e6%98%8e%e7%a1%ae" class="header-anchor"&gt;&lt;/a&gt;2. 一致性策略明确
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;写路径：强一致（通过事务和聚合保证）&lt;/li&gt;
&lt;li&gt;读路径：最终一致（允许短暂延迟）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;必须根据业务定义&amp;quot;可接受延迟范围&amp;quot;。例如：订单创建后 1 秒内可在查询中看到，这通常是可接受的。&lt;/p&gt;
&lt;p&gt;目的：用&amp;quot;可控不一致&amp;quot;换取系统性能与扩展性。&lt;/p&gt;
&lt;h4 id="3-模型分离"&gt;&lt;a href="#3-%e6%a8%a1%e5%9e%8b%e5%88%86%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;3. 模型分离
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;写模型（Domain Model）&lt;/strong&gt;：面向业务规则与一致性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读模型（Read Model）&lt;/strong&gt;：面向查询与展示，可以反范式化（去范式化）以提高查询效率&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读模型不需要与数据库表结构一致，它只为查询服务。&lt;/p&gt;
&lt;p&gt;目的：避免一个模型同时承担&amp;quot;复杂业务 + 高性能查询&amp;quot;两种职责。&lt;/p&gt;
&lt;h4 id="4-存储分离"&gt;&lt;a href="#4-%e5%ad%98%e5%82%a8%e5%88%86%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;4. 存储分离
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;写存储：事务型数据库（如 MySQL），保证强一致性&lt;/li&gt;
&lt;li&gt;读存储：缓存（如 Redis）/ 搜索引擎（如 ElasticSearch）/ 副本库&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;是否拆分存储取决于读压力与查询复杂度，并非所有场景都需要物理分离。&lt;/p&gt;
&lt;p&gt;目的：让存储层也服务于不同的优化目标。&lt;/p&gt;
&lt;h4 id="5-异步解耦"&gt;&lt;a href="#5-%e5%bc%82%e6%ad%a5%e8%a7%a3%e8%80%a6" class="header-anchor"&gt;&lt;/a&gt;5. 异步解耦
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;通过领域事件（Domain Event）连接读写模型&lt;/li&gt;
&lt;li&gt;流程：写操作 → 发布事件 → 异步更新读模型（Projection）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要接受最终一致性（存在延迟）。&lt;/p&gt;
&lt;p&gt;目的：降低系统耦合，支持独立扩展读模型。&lt;/p&gt;
&lt;h3 id="cqrs-架构概览"&gt;&lt;a href="#cqrs-%e6%9e%b6%e6%9e%84%e6%a6%82%e8%a7%88" class="header-anchor"&gt;&lt;/a&gt;CQRS 架构概览
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─────────────┐ Command ┌──────────────────┐ Event ┌──────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Client │ ──────────────→ │ Command Handler │ ─────────────→ │ Event Handler │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │ (写流程编排) │ │ (Projection) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└─────────────┘ └────────┬─────────┘ └────────┬─────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼ ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────────────┐ ┌──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ Write Store │ │ Read Store │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ (MySQL) │ │ (Redis/ES) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────────────┘ └──────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▲
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─────────────┐ Query ┌──────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Client │ ──────────────→ │ Query Handler │ ───────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │ (读流程编排) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└─────────────┘ └──────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="实现步骤"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e6%ad%a5%e9%aa%a4" class="header-anchor"&gt;&lt;/a&gt;实现步骤
&lt;/h2&gt;&lt;p&gt;CQRS 并不是一步到位的，可以根据系统复杂度分三个层次渐进式引入。不必一上来就用最复杂的方案。&lt;/p&gt;
&lt;h3 id="level-1逻辑分离单库"&gt;&lt;a href="#level-1%e9%80%bb%e8%be%91%e5%88%86%e7%a6%bb%e5%8d%95%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;Level 1：逻辑分离（单库）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;思路&lt;/strong&gt;：仍然使用单个数据库，只在代码层面分离读和写。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码中分为 Command 对象和 Query 对象&lt;/li&gt;
&lt;li&gt;写操作使用 Domain Model，读操作使用独立的 Read Model（DTO / VO）&lt;/li&gt;
&lt;li&gt;共享同一个数据库，读模型可以通过 SQL 视图或简单映射得到&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：系统初期，读写压力都不大，但希望代码职责清晰。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;：改动最小，风险最低
&lt;strong&gt;局限&lt;/strong&gt;：读和写仍然竞争同一份数据库资源&lt;/p&gt;
&lt;h3 id="level-2物理分离双库"&gt;&lt;a href="#level-2%e7%89%a9%e7%90%86%e5%88%86%e7%a6%bb%e5%8f%8c%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;Level 2：物理分离（双库）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;思路&lt;/strong&gt;：将数据库分为写库和读库，读库独立优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;写库（MySQL 主库）：处理所有写操作，保证强一致性&lt;/li&gt;
&lt;li&gt;读库（MySQL 从库 / Redis / ElasticSearch）：处理所有读操作&lt;/li&gt;
&lt;li&gt;写库数据通过主从复制或同步机制同步到读库&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：读多写少，查询逻辑较复杂，读操作需要独立扩容。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;：读写不竞争数据库资源，读库可针对性优化
&lt;strong&gt;局限&lt;/strong&gt;：需要维护数据同步机制，存在同步延迟&lt;/p&gt;
&lt;h3 id="level-3事件驱动event-sourcing--cqrs"&gt;&lt;a href="#level-3%e4%ba%8b%e4%bb%b6%e9%a9%b1%e5%8a%a8event-sourcing--cqrs" class="header-anchor"&gt;&lt;/a&gt;Level 3：事件驱动（Event Sourcing + CQRS）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;思路&lt;/strong&gt;：写路径产生的状态变化抽象为领域事件（Domain Event），由 Event Handler 异步构建和更新 Read Model。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;写操作修改 Domain Model 后，发布领域事件&lt;/li&gt;
&lt;li&gt;Event Handler 监听事件，将数据转换为读模型需要的格式，写入 Read Store&lt;/li&gt;
&lt;li&gt;读操作直接查询 Read Store，完全不访问 Write Store&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：业务逻辑复杂，需要完整的审计日志，或需要多个读模型服务不同查询场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;：读写完全解耦，可灵活构建多个专用读模型
&lt;strong&gt;局限&lt;/strong&gt;：架构最复杂，需要事件基础设施（消息队列），调试难度最高&lt;/p&gt;
&lt;h3 id="三种层次对比"&gt;&lt;a href="#%e4%b8%89%e7%a7%8d%e5%b1%82%e6%ac%a1%e5%af%b9%e6%af%94" class="header-anchor"&gt;&lt;/a&gt;三种层次对比
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;L1 逻辑分离&lt;/th&gt;
					&lt;th&gt;L2 物理分离&lt;/th&gt;
					&lt;th&gt;L3 事件驱动&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;存储数量&lt;/td&gt;
					&lt;td&gt;1 个&lt;/td&gt;
					&lt;td&gt;2 个&lt;/td&gt;
					&lt;td&gt;2+ 个&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;代码改动&lt;/td&gt;
					&lt;td&gt;小&lt;/td&gt;
					&lt;td&gt;中&lt;/td&gt;
					&lt;td&gt;大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;读写解耦程度&lt;/td&gt;
					&lt;td&gt;低&lt;/td&gt;
					&lt;td&gt;中&lt;/td&gt;
					&lt;td&gt;高&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;一致性&lt;/td&gt;
					&lt;td&gt;强一致&lt;/td&gt;
					&lt;td&gt;最终一致（秒级延迟）&lt;/td&gt;
					&lt;td&gt;最终一致（毫秒~秒级延迟）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;基础设施要求&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
					&lt;td&gt;主从复制 / 同步工具&lt;/td&gt;
					&lt;td&gt;消息队列 + Event Store&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;适用阶段&lt;/td&gt;
					&lt;td&gt;项目初期&lt;/td&gt;
					&lt;td&gt;系统成长期&lt;/td&gt;
					&lt;td&gt;大型复杂系统&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="示例--踩坑"&gt;&lt;a href="#%e7%a4%ba%e4%be%8b--%e8%b8%a9%e5%9d%91" class="header-anchor"&gt;&lt;/a&gt;示例 / 踩坑
&lt;/h2&gt;&lt;p&gt;下面通过一个电商订单系统，演示 CQRS Level 3（事件驱动）的完整实现。&lt;/p&gt;
&lt;h3 id="业务场景"&gt;&lt;a href="#%e4%b8%9a%e5%8a%a1%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;业务场景
&lt;/h3&gt;&lt;p&gt;电商订单系统，核心需求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;写&lt;/strong&gt;：创建订单、支付、取消（要求强一致性和复杂业务规则校验）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读&lt;/strong&gt;：订单列表、订单详情（用户频繁查询，需要低延迟）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：读多写少，查询涉及多表 join 和聚合计算&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cqrs-改造思路"&gt;&lt;a href="#cqrs-%e6%94%b9%e9%80%a0%e6%80%9d%e8%b7%af" class="header-anchor"&gt;&lt;/a&gt;CQRS 改造思路
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;写（Command）&lt;/strong&gt;：CreateOrder → OrderAggregate → MySQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读（Query）&lt;/strong&gt;：GetOrderDetail → Redis / Read DB（预聚合数据）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接&lt;/strong&gt;：OrderCreatedEvent → Event Handler 异步更新 Read Model&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="代码实现"&gt;&lt;a href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;代码实现
&lt;/h3&gt;&lt;h4 id="command写路径的输入模型"&gt;&lt;a href="#command%e5%86%99%e8%b7%af%e5%be%84%e7%9a%84%e8%be%93%e5%85%a5%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Command：写路径的输入模型
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 构造方法 / getter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="domain-model核心领域对象"&gt;&lt;a href="#domain-model%e6%a0%b8%e5%bf%83%e9%a2%86%e5%9f%9f%e5%af%b9%e8%b1%a1" class="header-anchor"&gt;&lt;/a&gt;Domain Model：核心领域对象
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;商品不能为空&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;productIds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CREATED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="command-handler写流程编排"&gt;&lt;a href="#command-handler%e5%86%99%e6%b5%81%e7%a8%8b%e7%bc%96%e6%8e%92" class="header-anchor"&gt;&lt;/a&gt;Command Handler：写流程编排
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EventPublisher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 构建领域对象（业务规则校验在 create 方法中）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProductIds&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 持久化（事务保证强一致性）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 发布事件（异步更新读模型）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderCreatedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="read-model读模型专为查询设计"&gt;&lt;a href="#read-model%e8%af%bb%e6%a8%a1%e5%9e%8b%e4%b8%93%e4%b8%ba%e6%9f%a5%e8%af%a2%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;Read Model：读模型（专为查询设计）
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderView&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productNames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// getter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意读模型和写模型的区别：写模型存储 &lt;code&gt;productIds&lt;/code&gt;（商品 ID），读模型存储 &lt;code&gt;productNames&lt;/code&gt;（商品名称）和 &lt;code&gt;totalPrice&lt;/code&gt;（总价）——这是面向查询优化的结果。&lt;/p&gt;
&lt;h4 id="query-handler读流程编排"&gt;&lt;a href="#query-handler%e8%af%bb%e6%b5%81%e7%a8%8b%e7%bc%96%e6%8e%92" class="header-anchor"&gt;&lt;/a&gt;Query Handler：读流程编排
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetOrderDetailHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderReadRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderView&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 直接查缓存或 Read DB，无业务逻辑&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="event-handler投影器projection"&gt;&lt;a href="#event-handler%e6%8a%95%e5%bd%b1%e5%99%a8projection" class="header-anchor"&gt;&lt;/a&gt;Event Handler：投影器（Projection）
&lt;/h4&gt;&lt;p&gt;Projection 监听领域事件，将 Command Side 的数据转换为 Read Model 并持久化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderProjectionHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderReadRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProductRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderCreatedEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productNames&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;productRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProductIds&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderView&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOrderId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProductNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productNames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="常见问题与踩坑"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98%e4%b8%8e%e8%b8%a9%e5%9d%91" class="header-anchor"&gt;&lt;/a&gt;常见问题与踩坑
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题 1：读模型更新延迟导致用户看不到最新数据&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是 CQRS 最常见的问题。用户刚创建订单，立刻查询却找不到。&lt;/p&gt;
&lt;p&gt;解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前端在写操作成功后，短暂轮询或使用 WebSocket 等待读模型更新&lt;/li&gt;
&lt;li&gt;在读模型未就绪时，回退到写库查询（Fallback 策略）&lt;/li&gt;
&lt;li&gt;在 UI 上提示用户&amp;quot;数据正在同步中&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;问题 2：事件处理失败导致读写数据不一致&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Event Handler 处理事件时可能失败（网络抖动、数据库异常等），导致读模型缺失数据。&lt;/p&gt;
&lt;p&gt;解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;事件消费必须实现幂等性，重复消费不会产生副作用&lt;/li&gt;
&lt;li&gt;使用消息队列的重试机制（如 RabbitMQ 的 Dead Letter Queue）&lt;/li&gt;
&lt;li&gt;定期做数据对账，比对写库和读库的数据差异并修复&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;问题 3：读模型结构变更困难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;业务变化导致读模型需要新增字段，但历史事件中不包含这些字段。&lt;/p&gt;
&lt;p&gt;解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新增字段设置默认值，避免历史数据缺失&lt;/li&gt;
&lt;li&gt;设计事件时采用&amp;quot;事件增量&amp;quot;模式，通过版本号管理字段变更&lt;/li&gt;
&lt;li&gt;需要时可以发起全量重建（Rebuild）读模型&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;问题 4：过度设计&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;并非所有系统都需要 CQRS。如果读写逻辑简单、并发量不大，引入 CQRS 反而增加不必要的复杂度。&lt;/p&gt;
&lt;p&gt;判断标准：当你的系统确实面临&amp;quot;读写性能无法兼顾&amp;quot;的瓶颈时，再考虑引入。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;本文介绍了 CQRS 模式的核心思想和实践方法，回顾要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;核心思路&lt;/strong&gt;：将 Command（写）和 Query（读）解耦，各自优化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;五个设计原则&lt;/strong&gt;：读写职责分离、一致性策略明确、模型分离、存储分离、异步解耦&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三种演进层次&lt;/strong&gt;：逻辑分离 → 物理分离 → 事件驱动，根据系统复杂度渐进式引入&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键取舍&lt;/strong&gt;：用最终一致性换取读写性能和扩展性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CQRS 不是银弹，它解决的是&amp;quot;读写性能无法兼顾&amp;quot;这一类特定问题。如果你的系统面临这个瓶颈，可以从 Level 1（逻辑分离）开始，渐进式演进到更复杂的方案。&lt;/p&gt;</description></item><item><title>CAP 定理：分布式系统的权衡</title><link>https://Orangex-position0.github.io/posts/distributed-systems/cap-theorem/</link><pubDate>Mon, 16 Mar 2026 13:00:00 +0800</pubDate><guid>https://Orangex-position0.github.io/posts/distributed-systems/cap-theorem/</guid><description>&lt;h2 id="cap-定理"&gt;&lt;a href="#cap-%e5%ae%9a%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;CAP 定理
&lt;/h2&gt;&lt;p&gt;CAP 定理指出，分布式系统最多只能同时满足以下三个特性中的两个：&lt;/p&gt;
&lt;h3 id="一致性-consistency"&gt;&lt;a href="#%e4%b8%80%e8%87%b4%e6%80%a7-consistency" class="header-anchor"&gt;&lt;/a&gt;一致性 (Consistency)
&lt;/h3&gt;&lt;p&gt;所有节点在同一时间看到相同的数据。&lt;/p&gt;
&lt;h3 id="可用性-availability"&gt;&lt;a href="#%e5%8f%af%e7%94%a8%e6%80%a7-availability" class="header-anchor"&gt;&lt;/a&gt;可用性 (Availability)
&lt;/h3&gt;&lt;p&gt;每个请求都能得到响应（成功或失败），保证系统持续服务。&lt;/p&gt;
&lt;h3 id="分区容错性-partition-tolerance"&gt;&lt;a href="#%e5%88%86%e5%8c%ba%e5%ae%b9%e9%94%99%e6%80%a7-partition-tolerance" class="header-anchor"&gt;&lt;/a&gt;分区容错性 (Partition Tolerance)
&lt;/h3&gt;&lt;p&gt;系统在网络分区的情况下仍能继续运行。&lt;/p&gt;
&lt;h2 id="三种权衡组合"&gt;&lt;a href="#%e4%b8%89%e7%a7%8d%e6%9d%83%e8%a1%a1%e7%bb%84%e5%90%88" class="header-anchor"&gt;&lt;/a&gt;三种权衡组合
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;组合&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
					&lt;th&gt;典型系统&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;CA&lt;/td&gt;
					&lt;td&gt;放弃分区容错&lt;/td&gt;
					&lt;td&gt;单机数据库（RDBMS）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CP&lt;/td&gt;
					&lt;td&gt;放弃可用性&lt;/td&gt;
					&lt;td&gt;HBase, MongoDB, Redis&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AP&lt;/td&gt;
					&lt;td&gt;放弃强一致性&lt;/td&gt;
					&lt;td&gt;Cassandra, DynamoDB&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="base-理论"&gt;&lt;a href="#base-%e7%90%86%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;BASE 理论
&lt;/h2&gt;&lt;p&gt;CAP 的替代方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Basically Available&lt;/strong&gt;：基本可用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soft state&lt;/strong&gt;：软状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eventually consistent&lt;/strong&gt;：最终一致性&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;在设计分布式系统时，需要根据业务场景选择合适的权衡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;金融交易：优先保证一致性（CP）&lt;/li&gt;
&lt;li&gt;社交媒体：优先保证可用性（AP）&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>