實(shí)戰(zhàn) 04〗分布式事務(wù)與最終一致性對(duì)賬:從“強(qiáng)鎖不現(xiàn)實(shí)”到“對(duì)賬兜底”)
一、物流系統(tǒng)為什么不能追求強(qiáng)一致物流微服務(wù)拆分后跨服務(wù)的數(shù)據(jù)一致性成為最棘手的工程問(wèn)題。庫(kù)存預(yù)扣成功但出庫(kù)單創(chuàng)建失敗、運(yùn)單狀態(tài)已更新但軌跡消息丟失、支付回調(diào)到達(dá)但訂單狀態(tài)未變——這些場(chǎng)景如果硬上兩階段提交2PC協(xié)調(diào)者一旦宕機(jī)所有參與方的數(shù)據(jù)庫(kù)連接會(huì)被瞬間卡死在高并發(fā)下是不可接受的?;ヂ?lián)網(wǎng)大廠普遍采用最終一致性核心邏輯是接受中間狀態(tài)的不一致用補(bǔ)償和對(duì)賬在可接受的時(shí)間窗口內(nèi)收斂到一致。物流場(chǎng)景尤其如此——庫(kù)存、運(yùn)單、軌跡、結(jié)算沒(méi)有哪一條鏈路需要“強(qiáng)一致到毫秒級(jí)”但每一條鏈路都必須“最終對(duì)得上”。二、第一層本地消息表 冪等消費(fèi)跨服務(wù)調(diào)用的原子性缺口最直接的解法是把“業(yè)務(wù)操作”和“消息發(fā)送”放進(jìn)同一個(gè)本地事務(wù)。以庫(kù)存預(yù)扣成功后發(fā)送出庫(kù)消息為例庫(kù)存服務(wù)在同一個(gè)數(shù)據(jù)庫(kù)事務(wù)中先更新庫(kù)存預(yù)扣記錄再寫(xiě)入一條待發(fā)送消息到本地消息表。事務(wù)提交后后臺(tái)任務(wù)輪詢(xún)消息表將消息投遞到Kafka投遞成功則標(biāo)記已發(fā)送。如果投遞失敗重試直到成功。消費(fèi)端必須做冪等。沒(méi)有冪等消息重試會(huì)導(dǎo)致重復(fù)扣減或重復(fù)建單。業(yè)務(wù)主鍵去重是最可靠的方式以“運(yùn)單號(hào) 操作類(lèi)型”作為冪等鍵消費(fèi)前先在Redis中占位已處理過(guò)的消息直接確認(rèn)并丟棄。更嚴(yán)格的做法是在消費(fèi)端建判重表將“業(yè)務(wù)更新”和“寫(xiě)判重記錄”放在同一個(gè)本地事務(wù)中。三、第二層狀態(tài)機(jī)兜底防止非法流轉(zhuǎn)冪等解決“重復(fù)”狀態(tài)機(jī)解決“亂序和非法狀態(tài)”。物流運(yùn)單的狀態(tài)流轉(zhuǎn)有嚴(yán)格的先后關(guān)系已簽收的運(yùn)單不能回退到派送中已取消的出庫(kù)單不能再次扣庫(kù)存。狀態(tài)機(jī)的職責(zé)是在每次狀態(tài)變更前校驗(yàn)當(dāng)前狀態(tài)是否允許遷移到目標(biāo)狀態(tài)不允許則靜默丟棄或告警而不是強(qiáng)行寫(xiě)入。這和分布式事務(wù)的關(guān)系在于狀態(tài)機(jī)是最后一道防線。即使補(bǔ)償邏輯出錯(cuò)、消息亂序到達(dá)狀態(tài)機(jī)也能攔住非法的狀態(tài)寫(xiě)入防止“已簽收”被覆蓋成“在途”這類(lèi)災(zāi)難性錯(cuò)誤。四、第三層定時(shí)對(duì)賬發(fā)現(xiàn)沉默的差異本地消息表和狀態(tài)機(jī)能處理“已知的失敗”但處理不了“沉默的丟失”。消息投遞成功但消費(fèi)端因?yàn)槲粗惓L^(guò)了處理、Redis預(yù)扣成功但數(shù)據(jù)庫(kù)更新失敗、第三方回調(diào)從未到達(dá)——這些差異不會(huì)被實(shí)時(shí)告警捕獲只能靠定時(shí)對(duì)賬發(fā)現(xiàn)。對(duì)賬的設(shè)計(jì)原則是按業(yè)務(wù)閉環(huán)比對(duì)而不是按單表比對(duì)。庫(kù)存對(duì)賬比的是“Redis預(yù)扣量 數(shù)據(jù)庫(kù)扣減量 釋放量”是否等于“已消耗量”運(yùn)單對(duì)賬比的是“OMS狀態(tài) WMS出庫(kù)狀態(tài) TMS簽收狀態(tài)”的三方一致性。差異進(jìn)入補(bǔ)償隊(duì)列自動(dòng)修復(fù)可修復(fù)的不可修復(fù)的升級(jí)到人工。專(zhuān)利文獻(xiàn)中的區(qū)塊鏈對(duì)賬方案給出了一個(gè)可參照的結(jié)構(gòu)交易雙方確認(rèn)完成后觸發(fā)智能合約進(jìn)行數(shù)據(jù)校驗(yàn)和自動(dòng)對(duì)賬對(duì)賬結(jié)果上鏈存儲(chǔ)。物流系統(tǒng)不需要區(qū)塊鏈但“雙方確認(rèn)后自動(dòng)比對(duì)、差異留痕”的邏輯是通用的。五、總結(jié)物流分布式事務(wù)的落地路徑可以概括為三層本地消息表保證消息不丟冪等消費(fèi)保證消息不重狀態(tài)機(jī)攔截非法狀態(tài)定時(shí)對(duì)賬發(fā)現(xiàn)沉默差異。強(qiáng)一致在物流場(chǎng)景中既不現(xiàn)實(shí)也不必要最終一致性的關(guān)鍵是每一層都有明確的補(bǔ)償路徑每一筆差異都能被追蹤到源頭。下一篇預(yù)告數(shù)據(jù)一致性是后端的事但車(chē)輛在路上跑路線怎么算最優(yōu)明天聊OR-Tools在車(chē)輛調(diào)度與路徑規(guī)劃中的工程落地。 本文作者深圳Eyf?半字節(jié)科技?技術(shù)產(chǎn)品團(tuán)隊(duì) 物流、快遞、快運(yùn)作業(yè)管理系統(tǒng)讓每一票都清晰可控。從任務(wù)分配到簽收回傳一站式管理每個(gè)作業(yè)環(huán)節(jié)。