<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文看懂微服務(wù)世界性技術(shù)難題——分布式事務(wù)丨IDCF

          共 8792字,需瀏覽 18分鐘

           ·

          2024-06-26 08:02

          點這里??星標(biāo)關(guān)注,獲取最新資訊!


          ??目錄

          1 問題簡述

          2 分析探討

          3 事務(wù)小結(jié)

          讓我們聊聊微服務(wù)的老大難:分布式事務(wù)。這是個已經(jīng)被無數(shù)次討論的問題,網(wǎng)上文章多如牛毛。本文從業(yè)務(wù)底層視角出發(fā),探討分布式事務(wù)究竟難在何處,以及務(wù)實的解決之路走向何方,再加一根牛毛... 不過希望本文是比較不一樣的視角,能給到讀者不同的啟發(fā)。

          在微服務(wù)架構(gòu)流行的背景下,分布式事務(wù)的文章多如牛毛,雖然很多將事務(wù)一致性與副本一致性混為一談,也仍不可否認其中相當(dāng)一部分文章、開源代碼,也還是不錯的。

          然而當(dāng)你躍躍欲試,期待將業(yè)界所謂成熟方案落地,可能很快就會發(fā)現(xiàn)現(xiàn)實的骨感——對于大量互聯(lián)網(wǎng)業(yè)務(wù),尤其是在大并發(fā)、大量使用nosql數(shù)據(jù)庫的微服務(wù)架構(gòu)下,很難落地。

          01、問題描述

          分布式事務(wù),即在分布式環(huán)境下,對于同一個事務(wù)下的幾件事情,要么一起完成,要么就和什么都沒發(fā)生一樣。也就是我們一般最關(guān)注的,是ACID中 的A,因為拆分了服務(wù)之后,每一步遠程 RPC 都可能故障。

          例如張三給李四轉(zhuǎn)賬,張三的錢扣了,李四的錢加了,這兩件事情,顯然就應(yīng)該一起完成,或者就都沒做。

          注意這里不要發(fā)散到Paxos、Raft等一致性協(xié)議上去,因為他們的領(lǐng)域本來就不同。

          Paxos針對的是多個節(jié)點重復(fù)做相同的一件事情,主要處理的是多副本之間的一致性:

          節(jié)點1完成任務(wù)1
          節(jié)點2完成任務(wù)1
          節(jié)點3完成任務(wù)1

          分布式事務(wù)則顯然不同,它針對的是有幾個不同的任務(wù),要捆綁一起完成或失敗,重點在整體的原子性:

          節(jié)點1完成任務(wù)1
          節(jié)點1完成任務(wù)2
          節(jié)點1完成任務(wù)3


          02、分析探討

          2.1

          超時的難題

          這里開門見山,讓我們不妨拋開具體的分析過程,直擊問題要害,那就是 —— 超時的處理。
          明確的成功 / 失敗都好說,但是超時呢,有可能成功了,也有可能沒成功。在大量現(xiàn)實場景下,只有明確地知道 A 和 B 的真實執(zhí)行情況,才能采取對應(yīng)的后續(xù)措施,這點比較顯而易見,此處不做展開。
          那我們要怎么知道 A 和 B 的真實執(zhí)行情況呢?“立字據(jù)” 成了必然的選擇,也就是應(yīng)該要能對賬反查。

          2.2

          對賬反查能力

          A和B這兩件事,是關(guān)聯(lián)到一個事務(wù)的,這就要求,要有一個唯一事務(wù)ID(這個ID還可以綁定一些其他附屬信息),我們可以通過這個ID來做對賬。如果能對賬,那么冪等也會順利成章就支持了。

          唯一的事務(wù)ID生成很簡單,如何通過這個ID來對賬才是關(guān)鍵所在。

          為了安全可靠,我們必須使用落地的信息來 “立字據(jù)”,ID要體現(xiàn)在落地的數(shù)據(jù)中,這樣我們就可以事后查詢相關(guān)數(shù)據(jù)得知真正執(zhí)行的情況,從而可以繼續(xù)將事務(wù)推進下去。

          接下來我們看不同情況下,落地信息具體如何記錄。

          2.3

          通過本地日志落地

          采用進程本地打日志的方式落地,可以配合網(wǎng)盤或者可靠日志采集等方式做集中化收集處理,這種做法其實也只能從事務(wù)角度記錄一些信息,至于業(yè)務(wù)數(shù)據(jù)本身是否明確修改 ok,在 DB timeout 時并不得而知,需要通過 WAL日志冗余多記錄一些東西(DB 修改前后的預(yù)期值都可以記錄),這樣無論DB操作成功與否,在一定的前提下,也能繼續(xù)推進處理(例如以鎖定用戶為前提,可以再將 db 數(shù)據(jù)查回來做核對,就知道是否操作 ok 了,更多討論參考本章第 9 小節(jié))。

          2.4

          SQL存儲的落地反查

          SQL存儲通常可以基于“本地事務(wù)”來做反查,類似 seata 之類的解決方案,建個undo log表,操作業(yè)務(wù)數(shù)據(jù)庫表 sql 的同時,會攔截綁定同庫內(nèi)undo log表的sql插入,一起作為一個本地事務(wù)來提交。也就是業(yè)務(wù)sql與undo log 表的 sql,能通過本地事務(wù)進行關(guān)聯(lián),保證一起成功和失敗,這樣通過反查 undo log 表,即可得知超時的 sql 到底是否執(zhí)行成功了。

          開啟本地事務(wù)  add 業(yè)務(wù)sql:具體業(yè)務(wù)邏輯對應(yīng)的 sql  add undolog 表sql:insert 一條 undo log,key 包含事務(wù) ID // 基于該表反查提交本地事務(wù)

          2.5 NoSQL存儲的落地反查

          NoSQL存儲不一定有本地事務(wù)能力,如果支持本地事務(wù)的話,那和sql差不多,綁定批量操作做事務(wù)就好了(redis 有支持,如果對一致性要求不是很苛刻,或許湊合能用)。

          如果不支持的話,這里提供兩種對賬的思路:

          1. 通過落地日志的方式,將ID和操作結(jié)果體現(xiàn)到流水日志中,然后通過準實時的接口支持查日志來判斷真正執(zhí)行的情況。這種做法,侵入了存儲訪問的 API,寫請求都帶上ID,且需要存儲服務(wù)支持流水查詢,自研的 kv 存儲可以考慮該方案。

          2. 直接將ID附著在存儲記錄里,例如NoSQL的value里面加一個事務(wù)ID 數(shù)組(然后按大小滾動,條數(shù)上限夠用就行),寫操作會留下ID的 “痕跡”。這種做法,侵入了業(yè)務(wù)表結(jié)構(gòu),表本身要掛這么一個數(shù)組。用 proto描述示例如下:

          // 業(yè)務(wù)表示例message ServiceTable{  repeated string txids = 1; // 事務(wù) id 數(shù)組,按一定 size 滾存  int32 field1 = 2;  int32 field2 = 3;  int32 field3 = 4;  int32 field4 = 5;  // ...}

          上述這兩種做法,顯然都談不上優(yōu)雅,但這樣也算是能建立基本的對賬能力,尤其值得一提的是方案 2),理論上適用于所有類型的kv存儲。

          2.6 外部接口

          如果分布式事務(wù)還關(guān)聯(lián)了外部接口調(diào)用,那外部接口的超時,則需要依賴合作方提供對賬、冪等能力。    

          2.7 微服務(wù)典型復(fù)雜場景

          對于典型復(fù)雜的微服務(wù)場景,這里舉例說明:

          事務(wù)開啟  任務(wù)1:改數(shù)據(jù) A // sql 存儲  任務(wù)2:改數(shù)據(jù) B // nosql 存儲  任務(wù)3:調(diào)用系統(tǒng)內(nèi)服務(wù)接口 C // -- 被調(diào)服務(wù)可能是類似情況  任務(wù)4:調(diào)用外部門服務(wù)接口 D事務(wù)結(jié)束

          假如我們通過一些手段,使得底層事務(wù)原子任務(wù)終于支持了對賬反查能力,采用哪種應(yīng)用層的事務(wù)方案,依然要三思。小規(guī)模業(yè)務(wù)或許本來對吞吐沒啥要求,可以積極嘗試。但大規(guī)模業(yè)務(wù),請求量大,數(shù)據(jù)復(fù)雜,事務(wù)底層涉及到的底層數(shù)據(jù)資源一排列組合,情況就非常復(fù)雜了,例如:

          // A B C D 四種數(shù)據(jù)并行事務(wù)一:改 A B C并行事務(wù)二:改 B D并行事務(wù)三:改 B A并行事務(wù)四:改 C D

          不難想象,這里事務(wù)鎖怎么加都是個問題,如果一把大鎖,吞吐顯然受限,如果細粒度鎖,那該如何設(shè)計對應(yīng)的粒度?所以對于大規(guī)模業(yè)務(wù),可能得特事特辦,例如僅僅是 cover 一下真的有強烈事務(wù)訴求的場景,其他場景下就直接有損處理。對應(yīng)上述偽碼,可能只有事務(wù)一繼續(xù)是事務(wù),其他三個就干脆不作為事務(wù)了。還有鎖的時效性問題,以及是否存在并發(fā)鎖,并發(fā)鎖要不要和事務(wù)鎖用同一把鎖,都要考慮。

          2.8 為什么DB層能做而業(yè)務(wù)不好做

          前面說了做好分布式事務(wù)的一些難處和前置條件,了解NewSQL的讀者可能會有這樣的疑問:TDSQL、TiDB他們不都已經(jīng)號稱支持了跨庫跨表的分布式事務(wù)了么,憑什么他們數(shù)據(jù)庫系統(tǒng)能做,怎么我們業(yè)務(wù)層反而為難了,不是總說數(shù)據(jù)庫才是高精尖么,他們不是應(yīng)該更難么?是啊,憑什么?

          這里的關(guān)鍵在于,數(shù)據(jù)庫系統(tǒng)將跨表事務(wù)這個問題收攏到它內(nèi)部了,關(guān)進了它自己的籠子里,然后它自己基于XA或其他協(xié)議,結(jié)合MVCC事務(wù)鎖之類的機制,就可以解決好這個問題。

          而在業(yè)務(wù)層面,潛在的參與方耦合面實在是太廣了,無法簡單收攏,同一個數(shù)據(jù)的變更觸發(fā)源太多了,很難參考DB層的解決方案??赡苡凶x者會認為,對于一個具體的數(shù)據(jù)表,最終將寫入操作也只收攏到一個點,確實如此,但是我們關(guān)注點并不僅僅在這個點,而是在上層的事務(wù)。例如SvrA管理了數(shù)據(jù)表A,但是SvrB、SvrC、SvrD都可以通過RPC接口來調(diào)用SvrA 進行數(shù)據(jù)表A的修改,還可以存在級聯(lián)的情況,例如SvrE先調(diào)用RPC訪問 SvrB,然后再由SvrB發(fā)起RPC調(diào)用SvrA做了數(shù)據(jù)表A的修改。

          SvrE ---|       SvrB----\       SvrC--- SvrA 數(shù)據(jù)表A       SvrD----/
          為了努力在業(yè)務(wù)層面解決事務(wù)問題,我們只好又祭出萬能大法,加一層或多層抽象,這也正是目前大量上層應(yīng)用解決方案做的:
          • 增加外在的事務(wù)存儲(主key為事務(wù)ID,記錄事務(wù)的參與方、進度、子事務(wù)等信息)。
          • 要求事務(wù)參與方遵循某些約束(如本地事務(wù) / 對賬能力),調(diào)用特定的一些 API 將事務(wù)信息進行上報關(guān)聯(lián)。
          • 引入事務(wù)協(xié)調(diào)者,由它來根據(jù)參與方上報的信息做一些事務(wù)的驅(qū)動邏輯。
          具體執(zhí)行的話,可能還要考慮事務(wù)鎖的具體實現(xiàn)策略(時效、粒度、與并發(fā)鎖的關(guān)系等),于是問題就會變得更加復(fù)雜。

          2.9 那犧牲一點可用性呢

          前面已經(jīng)提到,存儲層支持對賬反查,是分布式事務(wù)得以應(yīng)用的基本前提,那萬一存儲層就是不好支持對賬呢,還能怎么辦?

          在做分布式系統(tǒng)設(shè)計的時候,有個trick:一致性與可用性之間,有時是可以做置換的,當(dāng)想要保障一致性的時候,實在沒辦法,可以想想是否能用一些可用性來置換,反之亦然。

          那么我們在產(chǎn)生timeout類懸而未決異常的時候,可以犧牲一點可用性來加強事務(wù)的一致性保障么,粗想確實是可以的。例如TCC方案,try已經(jīng)都 ok了,到了confirm commit階段,有一個參與者其實并沒有及時完成 commit提交,它超時了。通過事務(wù)鎖,限制事務(wù)所涉及用戶的數(shù)據(jù)操作,在try階段加好鎖,直到這個懸而未決的事務(wù)完整執(zhí)行完再釋放即可。這里對用戶加鎖,本質(zhì)上就是犧牲可用性的做法,解鎖之前,其他寫該用戶數(shù)據(jù)的請求都處理不了。

          try 階段 lock 好confirm commit... 直至確定 ok 為止明確完成后才 unlock

          假如任由未confirm的數(shù)據(jù)繼續(xù)提供寫接口,則可能導(dǎo)致confirm時違反 try ok的條件(例如字段A符合一定條件),那不鎖定的話,在重試confirm的時候有可能字段A都已經(jīng)被改寫得不符合條件了,導(dǎo)致confirm實質(zhì)上失敗。

          有些特殊場合下,還可以不加鎖。例如對于金融轉(zhuǎn)賬而言,TCC還有一種預(yù)留凍結(jié)金額的做法,就可以不加鎖,但這顯然和業(yè)務(wù)強相關(guān),在很多業(yè)務(wù)場景下并不是一個簡單的整數(shù)字段操作,常常無法“預(yù)留資源”,不具普適性。

          簡而言之,就是要對事務(wù)關(guān)聯(lián)的用戶,規(guī)定一個明確的執(zhí)行序列(confirm、cancel 都可以),只要事務(wù)參與者活著,最終就一定會執(zhí)行完畢。同時這里要遵循可用性約束,就是該事務(wù)關(guān)聯(lián)用戶在完成相關(guān)執(zhí)行序列之前,不能放開別的請求修改對應(yīng)的數(shù)據(jù),否則可能會破壞數(shù)據(jù)一致性,即要鎖住用戶直至完成事務(wù)。

          我們再補充考慮下crash-safe以及restart-safe,為了能可靠的按執(zhí)行序列推進,顯然還需要考慮WAL日志(如果重啟有節(jié)點漂移,得用網(wǎng)盤),以及重啟后基于WAL的邏輯檢查和運行,而且這些其實都是很侵入代碼的工作。此外如果事務(wù)框架還想做一些重試,事務(wù)協(xié)調(diào)者讓事務(wù)參與者重試 commit,那相關(guān)數(shù)據(jù)的操作邏輯得有冪等能力。還需要指出的是,在分布式環(huán)境下,可能出現(xiàn)參與者沒收到try就收到了cancel(空回滾),或者收到了cancel之后才收到了try(懸掛)的情況,也都需要處理,也就是無論收到try還是cancel,都得先檢查一下相關(guān)事務(wù)的本地執(zhí)行情況。

          綜上來看,這樣做確實不一定要求DB層支持對賬,但必然要考慮鎖、WAL、冪等之類的問題,因此想要正確嚴謹?shù)貙崿F(xiàn)事務(wù)也并非易事,換言之,做是能做的,但要考慮細致,然后因為比較侵入,代碼復(fù)用性可能也不佳。如果不是很重要的業(yè)務(wù)場合,用上之后性價比不高,且任何機制疊加,還不可避免會帶來一些新的依賴和異常,維護成本也會增加。

          2.10 特定場景下的簡易實現(xiàn)

          有些業(yè)務(wù)場景,對事務(wù)隔離性沒什么要求,不必加鎖,最終一致即可,對時效性也要求不高,同時也能從數(shù)據(jù)本身的最新值體現(xiàn)出操作是否已成功(例如設(shè)置 vip,查回來發(fā)現(xiàn)還沒設(shè)置好就是沒成功的),那么此時還是有相對簡單的解決方案的。

          這類場景下,恰巧是天然的可反查、有冪等,例如對于設(shè)置 vip 這步而言:

          • 反查:直接查回來數(shù)據(jù)看下是否已經(jīng) vip 了,就知道執(zhí)行的結(jié)果。

          • 冪等:重新設(shè)置很多次 vip 效果也依然一樣是個 vip。

          具體細節(jié)此處就不再更多展開,無非就是結(jié)合反查持續(xù)重試直至成功即可。除了上面舉例的場景,也還會有各種其他的業(yè)務(wù)場景,都是可能采用折中有損方案應(yīng)對解決的,但因為太業(yè)務(wù)相關(guān),不具普適意義,這里不再發(fā)散。


          03、事務(wù)小結(jié)

          3.1 分布式事務(wù)的關(guān)鍵

          • 要有個唯一的事務(wù) ID,否則無法將各個子任務(wù)進行關(guān)聯(lián)。

          • 通常要基于事務(wù) ID 實現(xiàn)冪等或?qū)~,否則 timeout 沒法正確處理,也無法安全重試。

          • 一致性要求高的場景,會有對資源做鎖定或預(yù)留的做法,最終一致性要求的場景,則只要最終符合預(yù)期即可?;趯Y源要求的不同,會有一些常見的解決方案,例如多階段協(xié)商提交、TCC、事務(wù)消息等。

          現(xiàn)有的各種框架級別的解決方案,一般也就是:

          • 創(chuàng)建事務(wù),主 key 不必多說,肯定是事務(wù) ID 了,然后關(guān)聯(lián)一些事務(wù)信息的存儲。

          • 由協(xié)調(diào)者負責(zé)跟蹤推進完整個事務(wù)。

          • 各參與者遵從一定的規(guī)范約束,以冪等、對賬能力為基礎(chǔ),實現(xiàn)相應(yīng)的 API。

          對于鎖不鎖,鎖的粒度和時效性,是否能預(yù)留資源,則需要具體問題具體分析。這里提一種相對不侵入的解決方案 —— 異步對賬補償,參與者將事務(wù)信息匯報給協(xié)調(diào)者之后,就不再緊耦合,協(xié)調(diào)者可以自己獨立旁路慢慢跑對賬,然后按需 callback 回調(diào)業(yè)務(wù)進行補償處理,當(dāng)然其適用場景也是有限的。

          3.2 基于事務(wù)ID的冪等或?qū)~能力

          如果沒有相關(guān)能力,盲目引進事務(wù)上層應(yīng)用解決方案,只會讓系統(tǒng)更加復(fù)雜,而并沒有解決實際問題。

          • 如果是 nosql 存儲,可在 kv 的 value 結(jié)構(gòu)中增加事務(wù) ID 數(shù)組留痕來實現(xiàn)對賬。

          • 如果是 sql 存儲,就要看 sql 服務(wù)方有沒有提供基本的本地事務(wù)能力,如果是大規(guī)模 sql 存儲,還要確定好 sharding 策略。

          • 如果是外部接口,那就得外部接口支持相關(guān)能力。

          如果真的明確存儲層無法對賬,還是想基于 TCC 之類的方式去做,那務(wù)必小心謹慎,具體細節(jié)已于前一章的第 9 小節(jié)闡釋過。

          3.3 使用分布式事務(wù)的前置工作

          • 明確各個任務(wù)原子的冪等、對賬能力。
          • 明確需要采用怎樣的事務(wù)鎖機制(還要同時考慮并發(fā)、吞吐等要素)。
          • 明確業(yè)務(wù)本身對一致性的要求程度,是否可接受有損實現(xiàn),進程優(yōu)雅退出、故障重啟是否要考慮殘留事務(wù)的處理。
          • 充分選型,優(yōu)先考慮低侵入性的解決方案。

          3.4 結(jié)論


          雖然前文花了很大的篇幅,論述了分布式事務(wù)處理的復(fù)雜性 —— 有時著實復(fù)雜到讓人想要放棄,或許考慮做點異步對賬補償就是最經(jīng)濟實惠的解決方案。但如果業(yè)務(wù)場景真的需要,大家還是從實際出發(fā),該用就用。

          那些廣為流傳的方案能不能用呢,答案是看情況,只要場景匹配就行,但是在使用的時候,一定要清楚其背后的機制,明確其可能對吞吐帶來的影響,并遵循好必要的約定或規(guī)范。至于是否必要引入第三方的事務(wù)框架,也要具體情況具體分析,只要評估引入后確有價值,同時成本也能承受即可。

          總而言之,具體業(yè)務(wù)能不能使用分布式事務(wù),以及使用什么樣的解決方案,可以參考本文做充分的分析,實事求是,因地制宜。而好不好做(問題本身的難度復(fù)雜度),需不需要做(業(yè)務(wù)本身的核心訴求),想不想做(技術(shù)人員的主觀意愿),始終是幾個不同層面的問題。


          -End-
          原創(chuàng)作者|吳連火

          與其臨淵羨魚,不如退而結(jié)網(wǎng),用力擁抱夢想!?。?/span>

          《研發(fā)效能(DevOps)工程師》工信部教考中心-職業(yè)技術(shù)證書

          十二期班·開啟報名·7月20日正式開班!

          掌握端到端的研發(fā)效能知識體系,是想要成為高級管理者的必備技能!


          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产操b电影网站在线观看 | 婷婷综合五月天 | 亚洲v日本v欧美v久久精品 | 欧美精品久久久免费观看 | 精品无码免费一区二区三区 |