Update 分布式.md

补充 Saga 补偿流程和 RocketMQ 事务消息的示意图。
This commit is contained in:
CyC2018
2026-10-25 19:34:22 +08:00
parent 9994d82cb4
commit e3e2351a2f
5 changed files with 115 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+4
View File
@@ -210,6 +210,8 @@ TCC(Try-Confirm-Cancel)是在业务层面实现的两阶段提交,每个
Saga 将一个分布式事务拆分为多个本地事务,每个本地事务直接提交,并且都有一个对应的补偿操作。依次执行各个本地事务,如果某一个失败,就逆序执行前面已经提交的本地事务的补偿操作。
<div align="center"> <img src="pics/saga.png" width="540px"/> </div><br>
Saga 没有预留资源的 Try 阶段,中间状态对其它事务可见,因此没有隔离性。它适合流程长、无法长时间锁定资源的业务,正向操作和补偿操作同样需要实现幂等。
### 本地消息表
@@ -226,6 +228,8 @@ Saga 没有预留资源的 Try 阶段,中间状态对其它事务可见,因
RocketMQ 的事务消息把本地消息表的工作交给了消息队列:生产者先发送一条对消费者不可见的半消息,再执行本地事务,然后根据执行结果通知 Broker 提交或者回滚这条消息。如果 Broker 长时间没有收到通知,会主动回查生产者的本地事务状态。
<div align="center"> <img src="pics/rocketmq-transaction.png" width="540px"/> </div><br>
## 三、CAP
分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance)。常见的“三选二”说法有误导性,因为网络分区是无法避免的故障,不是可以选择的选项。
@@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="408" viewBox="0 0 600 408">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #666; font-size: 13px; }
.head { font-weight: bold; text-anchor: middle; }
.msg { text-anchor: middle; font-size: 12px; }
.note { font-size: 12px; fill: #C0605A; }
.ok { fill: #6A9F68; }
.title { font-weight: bold; font-size: 15px; fill: #777; text-anchor: middle; }
.life { stroke: #BBB; stroke-width: 1; stroke-dasharray: 4 4; }
.arrow { stroke: #666; stroke-width: 1.3; fill: none; marker-end: url(#a); }
.reply { stroke-dasharray: 5 4; }
.lost { stroke: #C0605A; marker-end: url(#x); }
.shadow { fill: #C0C0C0; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#666" stroke-width="1.6"/>
</marker>
<marker id="x" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#C0605A" stroke-width="1.6"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="300" y="28" class="title">RocketMQ 事务消息</text>
<line x1="100" y1="76" x2="100" y2="388" class="life"/>
<line x1="300" y1="76" x2="300" y2="388" class="life"/>
<line x1="500" y1="76" x2="500" y2="388" class="life"/>
<rect x="67" y="48" width="72" height="30" rx="6" class="shadow"/>
<rect x="64" y="45" width="72" height="30" rx="6" fill="#B0E3E6"/>
<text x="100" y="64.5" class="head">生产者</text>
<rect x="267" y="48" width="72" height="30" rx="6" class="shadow"/>
<rect x="264" y="45" width="72" height="30" rx="6" fill="#B1DDF0"/>
<text x="300" y="64.5" class="head">Broker</text>
<rect x="467" y="48" width="72" height="30" rx="6" class="shadow"/>
<rect x="464" y="45" width="72" height="30" rx="6" fill="#B0E3E6"/>
<text x="500" y="64.5" class="head">消费者</text>
<line x1="104" y1="100" x2="296" y2="100" class="arrow"/>
<text x="200" y="94" class="msg">① 发送半消息</text>
<line x1="296" y1="138" x2="104" y2="138" class="arrow reply"/>
<text x="200" y="132" class="msg">② 写入成功,消费者不可见</text>
<text x="110" y="180" class="note" text-anchor="start">③ 执行本地事务</text>
<line x1="104" y1="214" x2="296" y2="214" class="arrow"/>
<text x="200" y="208" class="msg">④ 根据事务结果提交或回滚</text>
<text x="300" y="256" class="note" text-anchor="middle">长时间没有收到 ④ 时:</text>
<line x1="296" y1="290" x2="104" y2="290" class="arrow reply"/>
<text x="200" y="284" class="msg">⑤ 回查本地事务状态</text>
<line x1="104" y1="328" x2="296" y2="328" class="arrow"/>
<text x="200" y="322" class="msg">⑥ 提交或回滚</text>
<line x1="304" y1="366" x2="496" y2="366" class="arrow"/>
<text x="400" y="360" class="msg">⑦ 提交后投递消息</text>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+60
View File
@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="220" viewBox="0 0 600 220">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #666; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #777; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.sub { font-size: 11px; fill: #888; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #666; stroke-width: 1.3; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #666; stroke-width: 1.3; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #C0C0C0; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#666" stroke-width="1.6"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#C0605A" stroke-width="1.6"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#6A9F68" stroke-width="1.6"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="300" y="28" class="title">Saga</text>
<rect x="73" y="63" width="110" height="40" rx="6" class="shadow"/>
<rect x="70" y="60" width="110" height="40" rx="6" fill="#B1DDF0"/>
<text x="125" y="78" class="b c">T1</text>
<text x="125" y="93" class="sub c">本地事务提交</text>
<rect x="233" y="63" width="110" height="40" rx="6" class="shadow"/>
<rect x="230" y="60" width="110" height="40" rx="6" fill="#B1DDF0"/>
<text x="285" y="78" class="b c">T2</text>
<text x="285" y="93" class="sub c">本地事务提交</text>
<rect x="393" y="63" width="110" height="40" rx="6" class="shadow"/>
<rect x="390" y="60" width="110" height="40" rx="6" fill="#F3C6C2"/>
<text x="445" y="78" class="b c">T3</text>
<text x="445" y="93" class="sub c">失败</text>
<line x1="183" y1="80" x2="227" y2="80" class="arrow "/>
<line x1="343" y1="80" x2="387" y2="80" class="arrow "/>
<rect x="73" y="153" width="110" height="40" rx="6" class="shadow"/>
<rect x="70" y="150" width="110" height="40" rx="6" fill="#B0E3E6"/>
<text x="125" y="168" class="b c">C1</text>
<text x="125" y="183" class="sub c">补偿 T1</text>
<rect x="233" y="153" width="110" height="40" rx="6" class="shadow"/>
<rect x="230" y="150" width="110" height="40" rx="6" fill="#B0E3E6"/>
<text x="285" y="168" class="b c">C2</text>
<text x="285" y="183" class="sub c">补偿 T2</text>
<path d="M445,103 L445,125 Q445,170 400,170 L345,170" class="arrow red"/>
<line x1="227" y1="170" x2="183" y2="170" class="arrow red"/>
<text x="520" y="84" class="sub">正向依次执行</text>
<text x="470" y="150" class="note">失败后逆序补偿</text>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB