技術(shù)支持 隨州網(wǎng)站建設(shè)永久不收費(fèi)的軟件app
文章目錄
- (一) 分布式理論算法和協(xié)議
- 1)CAP理論
- 總結(jié)
- 2)BASE理論
- BASE 理論的核心思想
- 基本可用
- 軟狀態(tài)
- 最終一致性
- 3)Paxos算法
- Basic Paxos 算法
- 4) Raft算法
- 1 拜占庭將軍
- 5)Gossip協(xié)議
- (二) 分布式鎖
- 分布式鎖應(yīng)該具備哪些條件
- 基于 redis 做分布式鎖
- (三) 分布式事務(wù)
- 強(qiáng)一致性
- 弱一致性
- 最終一致性
- 柔性事務(wù)
- 冪等操作
- 分布式事務(wù)使用場(chǎng)景
- 轉(zhuǎn)賬
- 下單扣庫(kù)存
- 同步超時(shí)
- 分布式事務(wù)的解決方案
- 1)兩階段提交/XA
- 本地消息表
- 可靠消息最終一致性
(一) 分布式理論算法和協(xié)議
1)CAP理論
CAP 理論/定理。起源于 2000 年,由加州大學(xué)伯克利分校的 Eric Brewer 教授在分布式計(jì)算原理研討會(huì)(PODC)上提出,因此 CAP 定理又被稱作 布魯爾定理(Brewer’s theorem)
CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分區(qū)容錯(cuò)性) 這三個(gè)單詞首字母組合。
在理論計(jì)算機(jī)科學(xué)中,CAP 定理(CAP theorem)指出對(duì)于一個(gè)分布式系統(tǒng)來(lái)說(shuō),當(dāng)設(shè)計(jì)讀寫操作時(shí),只能同時(shí)滿足以下三點(diǎn)中的兩個(gè):
- 一致性(Consistency) : 所有節(jié)點(diǎn)訪問(wèn)同一份最新的數(shù)據(jù)副本
- 可用性(Availability): 非故障的節(jié)點(diǎn)在合理的時(shí)間內(nèi)返回合理的響應(yīng)(不是錯(cuò)誤或者超時(shí)的響應(yīng))。
- 分區(qū)容錯(cuò)性(Partition Tolerance) : 分布式系統(tǒng)出現(xiàn)網(wǎng)絡(luò)分區(qū)的時(shí)候,仍然能夠?qū)ν馓峁┓?wù)。
當(dāng)發(fā)生網(wǎng)絡(luò)分區(qū)的時(shí)候,如果我們要繼續(xù)服務(wù),那么強(qiáng)一致性和可用性只能 2 選 1。也就是說(shuō)當(dāng)網(wǎng)絡(luò)分區(qū)之后 P 是前提,決定了 P 之后才有 C 和 A 的選擇。也就是說(shuō)分區(qū)容錯(cuò)性(Partition tolerance)我們是必須要實(shí)現(xiàn)的。
簡(jiǎn)而言之就是:CAP 理論中分區(qū)容錯(cuò)性 P 是一定要滿足的,在此基礎(chǔ)上,只能滿足可用性 A 或者一致性 C。
分布式系統(tǒng)理論上不可能選擇 CA 架構(gòu),只能選擇 CP 或者 AP 架構(gòu)。 比如 ZooKeeper、HBase 就是 CP 架構(gòu),Cassandra、Eureka 就是 AP 架構(gòu),Nacos 不僅支持 CP 架構(gòu)也支持 AP 架構(gòu)。
常見(jiàn)的可以作為注冊(cè)中心的組件有:ZooKeeper、Eureka、Nacos…。
- ZooKeeper 保證的是 CP。 任何時(shí)刻對(duì) ZooKeeper 的讀請(qǐng)求都能得到一致性的結(jié)果,但是, ZooKeeper 不保證每次請(qǐng)求的可用性比如在 Leader 選舉過(guò)程中或者半數(shù)以上的機(jī)器不可用的時(shí)候服務(wù)就是不可用的。
- Eureka 保證的則是 AP。 Eureka 在設(shè)計(jì)的時(shí)候就是優(yōu)先保證 A (可用性)。在 Eureka 中不存在什么 Leader 節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都是一樣的、平等的。因此 Eureka 不會(huì)像 ZooKeeper 那樣出現(xiàn)選舉過(guò)程中或者半數(shù)以上的機(jī)器不可用的時(shí)候服務(wù)就是不可用的情況。 Eureka 保證即使大部分節(jié)點(diǎn)掛掉也不會(huì)影響正常提供服務(wù),只要有一個(gè)節(jié)點(diǎn)是可用的就行了。只不過(guò)這個(gè)節(jié)點(diǎn)上的數(shù)據(jù)可能并不是最新的。
- Nacos 不僅支持 CP 也支持 AP。
總結(jié)
在進(jìn)行分布式系統(tǒng)設(shè)計(jì)和開(kāi)發(fā)時(shí),我們不應(yīng)該僅僅局限在 CAP 問(wèn)題上,還要關(guān)注系統(tǒng)的擴(kuò)展性、可用性等等
在系統(tǒng)發(fā)生“分區(qū)”的情況下,CAP 理論只能滿足 CP 或者 AP。要注意的是,這里的前提是系統(tǒng)發(fā)生了“分區(qū)”
如果系統(tǒng)沒(méi)有發(fā)生“分區(qū)”的話,節(jié)點(diǎn)間的網(wǎng)絡(luò)連接通信正常的話,也就不存在 P 了。這個(gè)時(shí)候,我們就可以同時(shí)保證 C 和 A 了。
總結(jié):如果系統(tǒng)發(fā)生“分區(qū)”,我們要考慮選擇 CP 還是 AP。如果系統(tǒng)沒(méi)有發(fā)生“分區(qū)”的話,我們要思考如何保證 CA 。
2)BASE理論
BASE 是 Basically Available(基本可用)、Soft-state(軟狀態(tài)) 和 Eventually Consistent(最終一致性) 三個(gè)短語(yǔ)的縮寫。BASE 理論是對(duì) CAP 中一致性 C 和可用性 A 權(quán)衡的結(jié)果,其來(lái)源于對(duì)大規(guī)?;ヂ?lián)網(wǎng)系統(tǒng)分布式實(shí)踐的總結(jié),是基于 CAP 定理逐步演化而來(lái)的,它大大降低了我們對(duì)系統(tǒng)的要求。
BASE 理論的核心思想
即使無(wú)法做到強(qiáng)一致性,但每個(gè)應(yīng)用都可以根據(jù)自身業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹?lái)使系統(tǒng)達(dá)到最終一致性。
也就是犧牲數(shù)據(jù)的一致性來(lái)滿足系統(tǒng)的高可用性,系統(tǒng)中一部分?jǐn)?shù)據(jù)不可用或者不一致時(shí),仍需要保持系統(tǒng)整體“主要可用”。
BASE 理論本質(zhì)上是對(duì) CAP 的延伸和補(bǔ)充,更具體地說(shuō),是對(duì) CAP 中 AP 方案的一個(gè)補(bǔ)充。
基本可用
基本可用是指分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時(shí)候,允許損失部分可用性。但是,這絕不等價(jià)于系統(tǒng)不可用。
什么叫允許損失部分可用性呢?
- 響應(yīng)時(shí)間上的損失: 正常情況下,處理用戶請(qǐng)求需要 0.5s 返回結(jié)果,但是由于系統(tǒng)出現(xiàn)故障,處理用戶請(qǐng)求的時(shí)間變?yōu)?3 s。
- 系統(tǒng)功能上的損失:正常情況下,用戶可以使用系統(tǒng)的全部功能,但是由于系統(tǒng)訪問(wèn)量突然劇增,系統(tǒng)的部分非核心功能無(wú)法使用。
軟狀態(tài)
軟狀態(tài)指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài)(CAP 理論中的數(shù)據(jù)不一致),并認(rèn)為該中間狀態(tài)的存在不會(huì)影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點(diǎn)的數(shù)據(jù)副本之間進(jìn)行數(shù)據(jù)同步的過(guò)程存在延時(shí)。
最終一致性
最終一致性強(qiáng)調(diào)的是系統(tǒng)中所有的數(shù)據(jù)副本,在經(jīng)過(guò)一段時(shí)間的同步后,最終能夠達(dá)到一個(gè)一致的狀態(tài)。因此,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達(dá)到一致,而不需要實(shí)時(shí)保證系統(tǒng)數(shù)據(jù)的強(qiáng)一致性。
分布式一致性的 3 種級(jí)別:
強(qiáng)一致性:系統(tǒng)寫入了什么,讀出來(lái)的就是什么。
弱一致性:不一定可以讀取到最新寫入的值,也不保證多少時(shí)間之后讀取到的數(shù)據(jù)是最新的,只是會(huì)盡量保證某個(gè)時(shí)刻達(dá)到數(shù)據(jù)一致的狀態(tài)。
最終一致性:弱一致性的升級(jí)版,系統(tǒng)會(huì)保證在一定時(shí)間內(nèi)達(dá)到數(shù)據(jù)一致的狀態(tài)。
業(yè)界比較推崇是最終一致性級(jí)別,但是某些對(duì)數(shù)據(jù)一致要求十分嚴(yán)格的場(chǎng)景比如銀行轉(zhuǎn)賬還是要保證強(qiáng)一致性。
3)Paxos算法
Paxos 算法是 Leslie Lamport在 1990 年提出了一種分布式系統(tǒng) 共識(shí) 算法。這也是第一個(gè)被證明完備的共識(shí)算法。
Paxos 算法是第一個(gè)被證明完備的分布式系統(tǒng)共識(shí)算法。共識(shí)算法的作用是讓分布式系統(tǒng)中的多個(gè)節(jié)點(diǎn)之間對(duì)某個(gè)提案(Proposal)達(dá)成一致的看法。提案的含義在分布式系統(tǒng)中十分寬泛,像哪一個(gè)節(jié)點(diǎn)是 Leader 節(jié)點(diǎn)、多個(gè)事件發(fā)生的順序等等都可以是一個(gè)提案。
Paxos 算法主要包含 2 個(gè)部分:
- Basic Paxos 算法:描述的是多節(jié)點(diǎn)之間如何就某個(gè)值(提案 Value)達(dá)成共識(shí)。
- Multi-Paxos 思想:描述的是執(zhí)行多個(gè) Basic Paxos 實(shí)例,就一系列值達(dá)成共識(shí)。Multi-Paxos 說(shuō)白了就是執(zhí)行多次 Basic Paxos ,核心還是 Basic Paxos 。
對(duì) Paxos 算法的定義做一個(gè)總結(jié):
- Paxos 算法是蘭伯特在 1990 年提出了一種分布式系統(tǒng)共識(shí)算法。
- 蘭伯特當(dāng)時(shí)提出的 Paxos 算法主要包含 2 個(gè)部分: Basic Paxos 算法和 Multi-Paxos 思想。
- Raft 算法、ZAB 協(xié)議、 Fast Paxos 算法都是基于 Paxos 算法改進(jìn)而來(lái)
Basic Paxos 算法
Basic Paxos 中存在 3 個(gè)重要的角色:
- 提議者(Proposer):也可以叫做協(xié)調(diào)者(coordinator),提議者負(fù)責(zé)接受客戶端的請(qǐng)求并發(fā)起提案。提案信息通常包括提案編號(hào) (Proposal ID) 和提議的值 (Value)。
- 接受者(Acceptor):也可以叫做投票員(voter),負(fù)責(zé)對(duì)提議者的提案進(jìn)行投票,同時(shí)需要記住自己的投票歷史;
- 學(xué)習(xí)者(Learner):如果有超過(guò)半數(shù)接受者就某個(gè)提議達(dá)成了共識(shí),那么學(xué)習(xí)者就需要接受這個(gè)提議,并就該提議作出運(yùn)算,然后將運(yùn)算結(jié)果返回給客戶端。
為了減少實(shí)現(xiàn)該算法所需的節(jié)點(diǎn)數(shù),一個(gè)節(jié)點(diǎn)可以身兼多個(gè)角色。并且,一個(gè)提案被選定需要被半數(shù)以上的 Acceptor 接受。這樣的話,Basic Paxos 算法還具備容錯(cuò)性,在少于一半的節(jié)點(diǎn)出現(xiàn)故障時(shí),集群仍能正常工作。
4) Raft算法
Raft 是 Multi-Paxos 的一個(gè)變種,其簡(jiǎn)化了 Multi-Paxos 的思想,變得更容易被理解以及工程實(shí)現(xiàn)。
針對(duì)沒(méi)有惡意節(jié)點(diǎn)的情況,除了 Raft 算法之外,當(dāng)前最常用的一些共識(shí)算法比如 ZAB 協(xié)議、 Fast Paxos 算法都是基于 Paxos 算法改進(jìn)的。
針對(duì)存在惡意節(jié)點(diǎn)的情況,一般使用的是 工作量證明(POW,Proof-of-Work)、 權(quán)益證明(PoS,Proof-of-Stake ) 等共識(shí)算法。這類共識(shí)算法最典型的應(yīng)用就是區(qū)塊鏈,就比如說(shuō)前段時(shí)間以太坊官方宣布其共識(shí)機(jī)制正在從工作量證明(PoW)轉(zhuǎn)變?yōu)闄?quán)益證明(PoS)。
1 拜占庭將軍
假設(shè)多位拜占庭將軍中沒(méi)有叛軍,信使的信息可靠但有可能被暗殺的情況下,將軍們?nèi)绾芜_(dá)成是否要進(jìn)攻的一致性決定?
解決方案大致可以理解成:先在所有的將軍中選出一個(gè)大將軍,用來(lái)做出所有的決定。
舉例如下:假如現(xiàn)在一共有 3 個(gè)將軍 A,B 和 C,每個(gè)將軍都有一個(gè)隨機(jī)時(shí)間的倒計(jì)時(shí)器,倒計(jì)時(shí)一結(jié)束,這個(gè)將軍就把自己當(dāng)成大將軍候選人,然后派信使傳遞選舉投票的信息給將軍 B 和 C,如果將軍 B 和 C 還沒(méi)有把自己當(dāng)作候選人(自己的倒計(jì)時(shí)還沒(méi)有結(jié)束),并且沒(méi)有把選舉票投給其他人,它們就會(huì)把票投給將軍 A,信使回到將軍 A 時(shí),將軍 A 知道自己收到了足夠的票數(shù),成為大將軍。在有了大將軍之后,是否需要進(jìn)攻就由大將軍 A 決定,然后再去派信使通知另外兩個(gè)將軍,自己已經(jīng)成為了大將軍。如果一段時(shí)間還沒(méi)收到將軍 B 和 C 的回復(fù)(信使可能會(huì)被暗殺),那就再重派一個(gè)信使,直到收到回復(fù)。
5)Gossip協(xié)議
Gossip 直譯過(guò)來(lái)就是閑話、流言蜚語(yǔ)的意思。流言蜚語(yǔ)有什么特點(diǎn)呢?容易被傳播且傳播速度還快,你傳我我傳他,然后大家都知道了。
Gossip 協(xié)議 也叫 Epidemic 協(xié)議(流行病協(xié)議)或者 Epidemic propagation 算法(疫情傳播算法),別名很多。不過(guò),這些名字的特點(diǎn)都具有 隨機(jī)傳播特性 (聯(lián)想一下病毒傳播、癌細(xì)胞擴(kuò)散等生活中常見(jiàn)的情景),這也正是 Gossip 協(xié)議最主要的特點(diǎn)。
Gossip 協(xié)議是一種允許在分布式系統(tǒng)中共享狀態(tài)的去中心化通信協(xié)議,通過(guò)這種通信協(xié)議,我們可以將信息傳播給網(wǎng)絡(luò)或集群中的所有成員。
(二) 分布式鎖
對(duì)于單機(jī)多線程來(lái)說(shuō),在 Java 中,我們通常使用 ReetrantLock
類、synchronized
關(guān)鍵字這類 JDK 自帶的 本地鎖 來(lái)控制一個(gè) JVM 進(jìn)程內(nèi)的多個(gè)線程對(duì)本地共享資源的訪問(wèn)。
- 當(dāng)在分布式模型下,數(shù)據(jù)只有一份(或有限制),此時(shí)需要利用鎖的技術(shù)控制某一時(shí)刻修改數(shù)據(jù)的進(jìn)程數(shù)。
- 與單機(jī)模式下的鎖不僅需要保證進(jìn)程可見(jiàn),還需要考慮進(jìn)程與鎖之間的網(wǎng)絡(luò)問(wèn)題。
- 分布式鎖還是可以將標(biāo)記存在內(nèi)存,只是該內(nèi)存不是某個(gè)進(jìn)程分配的內(nèi)存而是公共內(nèi)存如Redis、Memcache。至于利用數(shù)據(jù)庫(kù)、文件等做鎖與單機(jī)的實(shí)現(xiàn)是一樣的,只要保證標(biāo)記能互斥就行。
分布式鎖應(yīng)該具備哪些條件
一個(gè)最基本的分布式鎖需要滿足:
- 互斥:任意一個(gè)時(shí)刻,鎖只能被一個(gè)線程持有。
- 高可用:鎖服務(wù)是高可用的,當(dāng)一個(gè)鎖服務(wù)出現(xiàn)問(wèn)題,能夠自動(dòng)切換到另外一個(gè)鎖服務(wù)。并且,即使客戶端的釋放鎖的代碼邏輯出現(xiàn)問(wèn)題,鎖最終一定還是會(huì)被釋放,不會(huì)影響其他線程對(duì)共享資源的訪問(wèn)。這一般是通過(guò)超時(shí)機(jī)制實(shí)現(xiàn)的。
- 可重入:一個(gè)節(jié)點(diǎn)獲取了鎖之后,還可以再次獲取鎖。
除了上面這三個(gè)基本條件之外,一個(gè)好的分布式鎖還需要滿足下面這些條件: - 高性能:獲取和釋放鎖的操作應(yīng)該快速完成,并且不應(yīng)該對(duì)整個(gè)系統(tǒng)的性能造成過(guò)大影響。
- 非阻塞:如果獲取不到鎖,不能無(wú)限期等待,避免對(duì)系統(tǒng)正常運(yùn)行造成影響。
常見(jiàn)分布式鎖實(shí)現(xiàn)方案如下:
- 基于關(guān)系型數(shù)據(jù)庫(kù)比如 MySQL 實(shí)現(xiàn)分布式鎖。
- 基于分布式協(xié)調(diào)服務(wù) ZooKeeper 實(shí)現(xiàn)分布式鎖。
- 基于分布式鍵值存儲(chǔ)系統(tǒng)比如 Redis 、Etcd 實(shí)現(xiàn)分布式鎖。
關(guān)系型數(shù)據(jù)庫(kù)的方式一般是通過(guò)唯一索引或者排他鎖實(shí)現(xiàn)。不過(guò),一般不會(huì)使用這種方式,問(wèn)題太多比如性能太差、不具備鎖失效機(jī)制。
基于 ZooKeeper 或者 Redis 實(shí)現(xiàn)分布式鎖這兩種實(shí)現(xiàn)方式要用的更多一些
基于 redis 做分布式鎖
1)setnx()
setnx 的含義就是 SET if Not Exists,其主要有兩個(gè)參數(shù) setnx(key, value)。該方法是原子的,如key 不存在,則設(shè)置當(dāng)前 key 成功,返回 1;如果當(dāng)前 key 已經(jīng)存在,則設(shè)置當(dāng)前 key 失敗,返回 0。
2)expire()
expire 設(shè)置過(guò)期時(shí)間,要注意的是 setnx 命令不能設(shè)置 key 的超時(shí)時(shí)間,只能通過(guò) expire() 來(lái)對(duì)key 設(shè)置。
3) 使用步驟
1、setnx(lockkey, 1) 如果返回 0,則說(shuō)明占位失敗;如果返回 1,則說(shuō)明占位成功
2、expire() 命令對(duì) lockkey 設(shè)置超時(shí)時(shí)間,為的是避免死鎖問(wèn)題。
3、執(zhí)行完業(yè)務(wù)代碼后,可以通過(guò) delete 命令刪除 key。
這個(gè)方案其實(shí)是可以解決日常工作中的需求的,但從技術(shù)方案的探討上來(lái)說(shuō),可能還有一些可以完善的地方。比如,如果在第一步 setnx 執(zhí)行成功后,在 expire() 命令執(zhí)行成功前,發(fā)生了宕機(jī)的現(xiàn)象,那么就依然會(huì)出現(xiàn)死鎖的問(wèn)題,所以如果要對(duì)其進(jìn)行完善的話,可以使用 redis 的 setnx()、get() 和 getset() 方法來(lái)實(shí)現(xiàn)分布式鎖。
(三) 分布式事務(wù)
分布式事務(wù)是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點(diǎn)之上。例如在大型電商系統(tǒng)中,下單接口通常會(huì)扣減庫(kù)存、減去優(yōu)惠、生成訂單 id, 而訂單服務(wù)與庫(kù)存、優(yōu)惠、訂單 id 都是不同的服務(wù),下單接口的成功與否,不僅取決于本地的 db 操作,而且依賴第三方系統(tǒng)的結(jié)果,這時(shí)候分布式事務(wù)就保證這些操作要么全部成功,要么全部失敗。本質(zhì)上來(lái)說(shuō),分布式事務(wù)就是為了保證不同數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性。
強(qiáng)一致性
任何一次讀都能讀到某個(gè)數(shù)據(jù)的最近一次寫的數(shù)據(jù)。系統(tǒng)中的所有進(jìn)程,看到的操作順序,都和全局時(shí)鐘下的順序一致。簡(jiǎn)言之,在任意時(shí)刻,所有節(jié)點(diǎn)中的數(shù)據(jù)是一樣的。
弱一致性
數(shù)據(jù)更新后,如果能容忍后續(xù)的訪問(wèn)只能訪問(wèn)到部分或者全部訪問(wèn)不到,則是弱一致性。
最終一致性
不保證在任意時(shí)刻任意節(jié)點(diǎn)上的同一份數(shù)據(jù)都是相同的,但是隨著時(shí)間的遷移,不同節(jié)點(diǎn)上的同一份數(shù)據(jù)總是在向趨同的方向變化。簡(jiǎn)單說(shuō),就是在一段時(shí)間后,節(jié)點(diǎn)間的數(shù)據(jù)會(huì)最終達(dá)到一致?tīng)顟B(tài)。
柔性事務(wù)
不同于 ACID 的剛性事務(wù),在分布式場(chǎng)景下基于 BASE 理論,就出現(xiàn)了柔性事務(wù)的概念。要想通過(guò)柔性事務(wù)來(lái)達(dá)到最終的一致性,就需要依賴于一些特性,這些特性在具體的方案中不一定都要滿足,因?yàn)椴煌姆桨敢蟛灰粯?#xff1b;但是都不滿足的話,是不可能做柔性事務(wù)的。
冪等操作
在編程中一個(gè)冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。冪等函數(shù),或冪等方法,是指可以使用相同參數(shù)重復(fù)執(zhí)行,并能獲得相同結(jié)果的函數(shù)。這些函數(shù)不會(huì)影響系統(tǒng)狀態(tài),也不用擔(dān)心重復(fù)執(zhí)行會(huì)對(duì)系統(tǒng)造成改變。例如,支付流程中第三方支付系統(tǒng)告知系統(tǒng)中某個(gè)訂單支付成功,接收該支付回調(diào)接口在網(wǎng)絡(luò)正常的情況下無(wú)論操作多少次都應(yīng)該返回成功。
分布式事務(wù)使用場(chǎng)景
轉(zhuǎn)賬
轉(zhuǎn)賬是最經(jīng)典那的分布式事務(wù)場(chǎng)景,假設(shè)用戶 A 使用銀行 app 發(fā)起一筆跨行轉(zhuǎn)賬給用戶 B,銀行系統(tǒng)首先扣掉用戶 A 的錢,然后增加用戶 B 賬戶中的余額。此時(shí)就會(huì)出現(xiàn) 2 種異常情況:1. 用戶 A 的賬戶扣款成功,用戶 B 賬戶余額增加失敗 2. 用戶 A 賬戶扣款失敗,用戶 B 賬戶余額增加成功。對(duì)于銀行系統(tǒng)來(lái)說(shuō),以上 2 種情況都是不允許發(fā)生,此時(shí)就需要分布式事務(wù)來(lái)保證轉(zhuǎn)賬操作的成功。
下單扣庫(kù)存
在電商系統(tǒng)中,下單是用戶最常見(jiàn)操作。在下單接口中必定會(huì)涉及生成訂單 id, 扣減庫(kù)存等操作,對(duì)于微服務(wù)架構(gòu)系統(tǒng),訂單 id 與庫(kù)存服務(wù)一般都是獨(dú)立的服務(wù),此時(shí)就需要分布式事務(wù)來(lái)保證整個(gè)下單接口的成功
同步超時(shí)
繼續(xù)以電商系統(tǒng)為例,在微服務(wù)體系架構(gòu)下,我們的支付與訂單都是作為單獨(dú)的系統(tǒng)存在。訂單的支付狀態(tài)依賴支付系統(tǒng)的通知,假設(shè)一個(gè)場(chǎng)景:我們的支付系統(tǒng)收到來(lái)自第三方支付的通知,告知某個(gè)訂單支付成功,接收通知接口需要同步調(diào)用訂單服務(wù)變更訂單狀態(tài)接口,更新訂單狀態(tài)為成功。流程圖如下,從圖中可以看出有兩次調(diào)用,第三方支付調(diào)用支付服務(wù),以及支付服務(wù)調(diào)用訂單服務(wù),這兩步調(diào)用都可能出現(xiàn)調(diào)用超時(shí)的情況,此處如果沒(méi)有分布式事務(wù)的保證,就會(huì)出現(xiàn)用戶訂單實(shí)際支付情況與最終用戶看到的訂單支付情況不一致的情況。
分布式事務(wù)的解決方案
1)兩階段提交/XA
兩階段提交,顧名思義就是要分兩步提交。存在一個(gè)負(fù)責(zé)協(xié)調(diào)各個(gè)本地資源管理器的事務(wù)管理器,本地資源管理器一般是由數(shù)據(jù)庫(kù)實(shí)現(xiàn),事務(wù)管理器在第一階段的時(shí)候詢問(wèn)各個(gè)資源管理器是否都就緒?如果收到每個(gè)資源的回復(fù)都是 yes,則在第二階段提交事務(wù),如果其中任意一個(gè)資源的回復(fù)是 no, 則回滾事務(wù)。
大致的流程:
第一階段(prepare):事務(wù)管理器向所有本地資源管理器發(fā)起請(qǐng)求,詢問(wèn)是否是 ready 狀態(tài),所有參與者都將本事務(wù)能否成功的信息反饋發(fā)給協(xié)調(diào)者;
第二階段 (commit/rollback):事務(wù)管理器根據(jù)所有本地資源管理器的反饋,通知所有本地資源管理器,步調(diào)一致地在所有分支上提交或者回滾。
存在的問(wèn)題:
同步阻塞:當(dāng)參與事務(wù)者存在占用公共資源的情況,其中一個(gè)占用了資源,其他事務(wù)參與者就只能阻塞等待資源釋放,處于阻塞狀態(tài)。
單點(diǎn)故障:一旦事務(wù)管理器出現(xiàn)故障,整個(gè)系統(tǒng)不可用
數(shù)據(jù)不一致:在階段二,如果事務(wù)管理器只發(fā)送了部分 commit 消息,此時(shí)網(wǎng)絡(luò)發(fā)生異常,那么只有部分參與者接收到 commit 消息,也就是說(shuō)只有部分參與者提交了事務(wù),使得系統(tǒng)數(shù)據(jù)不一致。
不確定性:當(dāng)協(xié)事務(wù)管理器發(fā)送 commit 之后,并且此時(shí)只有一個(gè)參與者收到了 commit,那么當(dāng)該參與者與事務(wù)管理器同時(shí)宕機(jī)之后,重新選舉的事務(wù)管理器無(wú)法確定該條消息是否提交成功。
本地消息表
本地消息表這個(gè)方案最初是 ebay 架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章。該方案中會(huì)有消息生產(chǎn)者與消費(fèi)者兩個(gè)角色,假設(shè)系統(tǒng) A 是消息生產(chǎn)者,系統(tǒng) B 是消息消費(fèi)者,其大致流程如下:
- 當(dāng)系統(tǒng) A 被其他系統(tǒng)調(diào)用發(fā)生數(shù)據(jù)庫(kù)表更操作,首先會(huì)更新數(shù)據(jù)庫(kù)的業(yè)務(wù)表,其次會(huì)往相同數(shù)據(jù)庫(kù)的消息表中插入一條數(shù)據(jù),兩個(gè)操作發(fā)生在同一個(gè)事務(wù)中
- 系統(tǒng) A 的腳本定期輪詢本地消息往 mq 中寫入一條消息,如果消息發(fā)送失敗會(huì)進(jìn)行重試
- 系統(tǒng) B 消費(fèi) mq 中的消息,并處理業(yè)務(wù)邏輯。如果本地事務(wù)處理失敗,會(huì)在繼續(xù)消費(fèi) mq 中的消息進(jìn)行重試,如果業(yè)務(wù)上的失敗,可以通知系統(tǒng) A 進(jìn)行回滾操作
本地消息表實(shí)現(xiàn)的條件:
- 消費(fèi)者與生成者的接口都要支持冪等
- 生產(chǎn)者需要額外的創(chuàng)建消息表
- 需要提供補(bǔ)償邏輯,如果消費(fèi)者業(yè)務(wù)失敗,需要生產(chǎn)者支持回滾操作
容錯(cuò)機(jī)制:
- 步驟 1 失敗時(shí),事務(wù)直接回滾
- 步驟 2、3 寫 mq 與消費(fèi) mq 失敗會(huì)進(jìn)行重試
- 步驟 3 業(yè)務(wù)失敗系統(tǒng) B 向系統(tǒng) A 發(fā)起事務(wù)回滾操作
此方案的核心是將需要分布式處理的任務(wù)通過(guò)消息日志的方式來(lái)異步執(zhí)行。消息日志可以存儲(chǔ)到本地文本、數(shù)據(jù)庫(kù)或消息隊(duì)列,再通過(guò)業(yè)務(wù)規(guī)則自動(dòng)或人工發(fā)起重試。人工重試更多的是應(yīng)用于支付場(chǎng)景,通過(guò)對(duì)賬系統(tǒng)對(duì)事后問(wèn)題的處理。
可靠消息最終一致性
- A 系統(tǒng)先向 mq 發(fā)送一條 prepare 消息,如果 prepare 消息發(fā)送失敗,則直接取消操作
- 如果消息發(fā)送成功,則執(zhí)行本地事務(wù)
- 如果本地事務(wù)執(zhí)行成功,則想 mq 發(fā)送一條 confirm 消息,如果發(fā)送失敗,則發(fā)送回滾消息
- B 系統(tǒng)定期消費(fèi) mq 中的 confirm 消息,執(zhí)行本地事務(wù),并發(fā)送 ack 消息。如果 B 系統(tǒng)中的本地事務(wù)失敗,會(huì)一直不斷重試,如果是業(yè)務(wù)失敗,會(huì)向 A 系統(tǒng)發(fā)起回滾請(qǐng)求
- mq 會(huì)定期輪詢所有 prepared 消息調(diào)用系統(tǒng) A 提供的接口查詢消息的處理情況,如果該 prepare 消息本地事務(wù)處理成功,則重新發(fā)送 confirm 消息,否則直接回滾該消息
該方案與本地消息最大的不同是去掉了本地消息表,其次本地消息表依賴消息表重試寫入 mq 這一步由本方案中的輪詢 prepare 消息狀態(tài)來(lái)重試或者回滾該消息替代。其實(shí)現(xiàn)條件與余容錯(cuò)方案基本一致。目前市面上實(shí)現(xiàn)該方案的只有阿里的 RocketMq。