逸仙电商Seata企业级落地实践
作者 | 張嘉偉(GitHub ID:l81893521)
就職于逸仙電商交易中心;Seata Committer,加入 Seata 社區(qū)已有一年半,見(jiàn)證了從 Fescar 到 Seata 的變更,GA等。
你可能沒(méi)有聽(tīng)說(shuō)過(guò)逸仙電商,但是你的女朋友不可能沒(méi)有聽(tīng)說(shuō)過(guò)它。逸仙電商旗下有完美日記、小奧汀、完子心選等品牌。完美日記作為國(guó)貨美妝界的黑馬用了不到三年時(shí)間,達(dá)到了行業(yè)龍頭企業(yè)通常需要十年以上才能達(dá)到的營(yíng)收規(guī)模。2020 年正式登陸紐約證券交易所,成為第一家在美國(guó)上市的“國(guó)貨美妝品牌”。在快速增長(zhǎng)的業(yè)務(wù)下,系統(tǒng)流量增長(zhǎng)速度越來(lái)越快,服務(wù)數(shù)量不斷增多,調(diào)用鏈路錯(cuò)綜復(fù)雜,數(shù)據(jù)不一致的問(wèn)題日漸顯現(xiàn),為了降低人力成本和系統(tǒng)資源,我們選擇了 Seata。
本文將會(huì)以逸仙電商的業(yè)務(wù)作為背景, 先介紹一下seata的原理, 并給大家進(jìn)行線(xiàn)上演示, 由淺入深去介紹這款中間件, 以便讀者更加容易去理解 Seata 這個(gè)中間件。
1. 問(wèn)題背景
在微服務(wù)的架構(gòu)下,數(shù)據(jù)不一致的產(chǎn)生原因
2. 業(yè)務(wù)介紹
挑選了逸仙電商一些比較簡(jiǎn)單易懂的業(yè)務(wù)作為開(kāi)展背景
3. 原理分析
Seata的實(shí)現(xiàn)原理和故障解決以及部署方案
4. Demo演示
如何在線(xiàn)體驗(yàn)這款中間件,無(wú)需整合和下載任何代碼
數(shù)據(jù)不一致的原因
在微服務(wù)的環(huán)境下,由于調(diào)用鏈路跨越多個(gè)應(yīng)用,甚至跨越多個(gè)數(shù)據(jù)源,數(shù)據(jù)的一致性在普通情況下難以保證,導(dǎo)致數(shù)據(jù)不一致的原因非常多,這里列舉了三個(gè)最常見(jiàn)的原因
這里挑選了逸仙電商業(yè)務(wù)體系里面一個(gè)非常通俗容易理解的調(diào)用方式,并且去掉了多余復(fù)雜的鏈路,方便在閱讀過(guò)程中更加關(guān)注重點(diǎn)。
在以往如果出現(xiàn)數(shù)據(jù)不一致的問(wèn)題,相信大多數(shù)的解決方案是這樣的
- 人工補(bǔ)償數(shù)據(jù)
- 定時(shí)任務(wù)檢查和補(bǔ)償數(shù)據(jù)
但是這兩種方式的缺點(diǎn)也是顯然意見(jiàn)的,一種是浪費(fèi)大量的人力成本和時(shí)間,另外一種是浪費(fèi)大量的系統(tǒng)資源去檢查數(shù)據(jù)是否一致和額外的人力成本。
接下來(lái)我會(huì)根據(jù)逸仙在生產(chǎn)上穩(wěn)定運(yùn)行將近一年總結(jié)的經(jīng)驗(yàn)并且盡可能簡(jiǎn)單的去描述Seata是如何保證數(shù)據(jù)一致的。
原理
在接觸一項(xiàng)新技術(shù)之前,我們應(yīng)該先從宏觀(guān)的角度去理解它大概包含些什么。在Seata中,它大概分為以下三個(gè)角色。
- 黃色,Transaction Manager(TM),client端
- 藍(lán)色,Resource Manager(RM),client端
- 綠色,Transaction Coordinator(TC),server端
你可以根據(jù)顏色,名字,縮寫(xiě)甚至客戶(hù)端/服務(wù)端去區(qū)分這三者的關(guān)系,同時(shí)簡(jiǎn)單去理解它們每一個(gè)自身的職責(zé)大概是要干些什么事情,后面的講解我也會(huì)保持一樣的顏色和名字來(lái)區(qū)分它們。
Seata其中只一個(gè)核心是數(shù)據(jù)源代理,意味著在你執(zhí)行一句Sql語(yǔ)句時(shí),Seata會(huì)幫你在執(zhí)行之前和之后做一些額外的操作,從而保證數(shù)據(jù)的一致性,并且盡可能做到無(wú)感知,讓你使用起來(lái)感覺(jué)非常方便和神奇。這里首先要去理解兩個(gè)知識(shí)點(diǎn)。
- 前置鏡像(Before Image):保存數(shù)據(jù)變更前的樣子
- 后置鏡像(After Image):保存數(shù)據(jù)變更后的樣子
- Undo Log:保存鏡像
有時(shí)候新項(xiàng)目接入的時(shí)候,有同事會(huì)問(wèn),為什么事務(wù)不生效,如果你也遇到過(guò)同樣的問(wèn)題,那首先要檢查一下自己的數(shù)據(jù)源是否已經(jīng)代理成功。
當(dāng)執(zhí)行一句Sql時(shí),Seata會(huì)嘗試去獲取這條/批數(shù)據(jù)變更前的內(nèi)容,并保存到前置鏡像中(Insert語(yǔ)句沒(méi)有前置鏡像),然后執(zhí)行業(yè)務(wù)Sql,執(zhí)行完后會(huì)嘗試去獲取這條/批數(shù)據(jù)變更后的內(nèi)容,并保存到后置鏡像中(Delete語(yǔ)句沒(méi)有后置鏡像),之后會(huì)進(jìn)行分支事務(wù)注冊(cè),TC在收到分支事務(wù)注冊(cè)請(qǐng)求時(shí),會(huì)持久化這些分支事務(wù)信息和根據(jù)操作數(shù)據(jù)的主鍵為維度作為全局鎖并持久化,可選持久化方式有
- file
- db
- redis
在收到TC返回的分支注冊(cè)成功響應(yīng)后,會(huì)把鏡像持久化到應(yīng)用所在的數(shù)據(jù)源的Undo Log表中,最后提交本地事務(wù)。
以上所有操作都會(huì)保證在同一個(gè)本地事務(wù)中,保證業(yè)務(wù)操作和Undo Log操作的原子性
一階段
理解了單個(gè)應(yīng)用的處理流程,再?gòu)囊粋€(gè)完全的調(diào)用鏈路,去看Seata的處理過(guò)程,相信理解起來(lái)會(huì)簡(jiǎn)單很多,
直到目前,一階段已經(jīng)執(zhí)行完成。
另外一個(gè)需要注意的問(wèn)題是,如果發(fā)現(xiàn)事務(wù)不生效,需要檢查XID是否成功往下傳遞
二階段提交
如果在整個(gè)調(diào)用鏈路的過(guò)程,沒(méi)有發(fā)生任何異常,那么二階段提交的過(guò)程是非常簡(jiǎn)單而且非常的高效,只有兩步
- TC清理全局事務(wù)對(duì)應(yīng)的信息
- RM清理對(duì)應(yīng)Undo Log信息
二階段回滾
若調(diào)用過(guò)程中出現(xiàn)異常,會(huì)自動(dòng)觸發(fā)反向回滾
反向回滾表示,如果調(diào)用鏈路順序?yàn)?A -> B -> C,那么回滾順序?yàn)?C -> B -> A。
例:A=Insert,B=Update,如果回滾時(shí)不按照反向的順序進(jìn)行回滾,則有可能出現(xiàn)回滾時(shí)先把A刪除了,再更新A,引發(fā)錯(cuò)誤
在回滾的過(guò)程中有可能會(huì)遇到一種非常極端的情況,回滾到對(duì)應(yīng)的模塊時(shí),找不到對(duì)應(yīng)的Undo Log,這種情況主要發(fā)生在
- 分支事務(wù)注冊(cè)成功,但是由于網(wǎng)絡(luò)原因收不到成功的響應(yīng),Undo Log未被持久化
- 同時(shí)全局事務(wù)超時(shí)(超時(shí)時(shí)間可自由配置)觸發(fā)回滾
這時(shí)候RM會(huì)持久化一個(gè)特殊的Undo Log,狀態(tài)為GlobalFinished。由于這個(gè)全局事務(wù)已經(jīng)回滾,需要防止網(wǎng)絡(luò)恢復(fù)時(shí),未持久化Undo Log的應(yīng)用收到了分支注冊(cè)成功的響應(yīng)和持久化Undo Log,并提交本地最終引發(fā)的數(shù)據(jù)不一致。
讀已提交
由于在一階段的時(shí)候,數(shù)據(jù)已經(jīng)保存到數(shù)據(jù)庫(kù)并提交,所以Seata默認(rèn)的隔離級(jí)別為讀未提交,如果需要把隔離級(jí)別提升至讀已提交則需要使用@GlobalLock標(biāo)簽并且在查詢(xún)語(yǔ)句上加上for update
這個(gè)時(shí)候Seata會(huì)對(duì)添加了for update的查詢(xún)語(yǔ)句進(jìn)行代理
如果一個(gè)全局事務(wù)1正在操作,并且未進(jìn)行二階段提交/回滾的時(shí)候,全局鎖是被全局事務(wù)1鎖持有的,同時(shí)另外一個(gè)全局事務(wù)2嘗試去查詢(xún)相同的數(shù)據(jù),由于查詢(xún)語(yǔ)句被代理,seata會(huì)嘗試去獲取這條數(shù)據(jù)的全局鎖,直到獲取成功/失敗(重試次數(shù)達(dá)到配置值)為止。
問(wèn)題
在生產(chǎn)上運(yùn)行接近1年時(shí)間,總體來(lái)說(shuō)遇到的問(wèn)題不算多,解決起來(lái)也比較容易,比如以下這個(gè)問(wèn)題
經(jīng)過(guò)排查發(fā)現(xiàn),由于Seata會(huì)使用jdbc標(biāo)準(zhǔn)接口嘗試獲取業(yè)務(wù)操作所對(duì)應(yīng)的表結(jié)構(gòu),由于表結(jié)構(gòu)改動(dòng)頻率較少,并且考慮到表結(jié)構(gòu)變更后應(yīng)用會(huì)進(jìn)行重啟,所以會(huì)對(duì)表結(jié)構(gòu)進(jìn)行緩存,如果表結(jié)構(gòu)改動(dòng)后不對(duì)應(yīng)用進(jìn)行重啟,有可能引發(fā)構(gòu)建鏡像時(shí)出現(xiàn)NullPointerException。下面貼出關(guān)鍵代碼
修改表結(jié)構(gòu),需要對(duì)應(yīng)用進(jìn)行重啟,即可解決此問(wèn)題,非常簡(jiǎn)單
第二個(gè)遇到的問(wèn)題就是在生產(chǎn)運(yùn)行一段時(shí)間后,發(fā)現(xiàn)branch_table和lock_table存在數(shù)據(jù)殘留,并且根據(jù)xid查詢(xún)global_table沒(méi)有對(duì)應(yīng)的數(shù)據(jù),導(dǎo)致后續(xù)操作相同的數(shù)據(jù)行會(huì)出現(xiàn)獲取全局鎖失敗,并且會(huì)每隔一段時(shí)間小量出現(xiàn)。這個(gè)異常隱藏的比較深,而且在開(kāi)發(fā)環(huán)境和測(cè)試環(huán)境無(wú)法復(fù)現(xiàn),通過(guò)跟蹤源碼和總結(jié)原因發(fā)現(xiàn),是由于開(kāi)啟了Mysql主從,導(dǎo)致提交/回滾時(shí),Seata通過(guò)xid查詢(xún)分支事務(wù)時(shí),數(shù)據(jù)未同步到從庫(kù),導(dǎo)致遺漏了一部分分支事務(wù)數(shù)據(jù)。
源碼部分
相信此問(wèn)題會(huì)在支持Raft之后得到完美的解決
pr: https://github.com/seata/seata/pull/3086
有興趣的朋友也可以嘗試去review一下代碼
部署-高可用
Seata和其他中間件的高可用部署方式差別不大,如圖片所示,確保應(yīng)用服務(wù)和TC訪(fǎng)問(wèn)相同的注冊(cè)中心和配置中心,同時(shí)只需要啟動(dòng)多臺(tái)TC,并將store.mode改為db模式即可完成高可用部署,并選擇合適的注冊(cè)中心和配置中心即可,目前支持的配置中心有
- nacos
- consul
- etcd3
- eureka
- redis
- sofa
- zookeeper
可選的配置中心有
- nacos
- etcd3
- consul
- apollo
- zk
部署-單節(jié)點(diǎn)多應(yīng)用
當(dāng)然也有更加靈活的部署方式,通過(guò)vgoup-mapping(事務(wù)集群),可以做到單節(jié)點(diǎn)多應(yīng)用的隔離,比如A應(yīng)用和B應(yīng)用訪(fǎng)問(wèn)A-Group的兩個(gè)TC,C應(yīng)用和D應(yīng)用訪(fǎng)問(wèn)B-Group的兩個(gè)TC,E應(yīng)用和F應(yīng)用訪(fǎng)問(wèn)C-Group的兩個(gè)TC。
部署-異地容災(zāi)
通過(guò)vgoup-mapping也可以做到異地容災(zāi),當(dāng)原有集群出現(xiàn)不可用時(shí),可以通過(guò)變更配置立刻轉(zhuǎn)移到備用的集群上。此處以Nacos作為注冊(cè)中心舉例,TC配置方式如下:
Demo
最后通過(guò)訪(fǎng)問(wèn)阿里云知行動(dòng)手首頁(yè),即可在線(xiàn)快速體驗(yàn)各種各樣的中間件:
https://start.aliyun.com
Seata直達(dá)傳送門(mén),無(wú)需下載代碼,在線(xiàn)編譯和部署:
https://start.aliyun.com/handson/isnEO76f/distributedtransaction
原文鏈接:https://developer.aliyun.com/article/783527?
版權(quán)聲明:本文內(nèi)容由阿里云實(shí)名注冊(cè)用戶(hù)自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,阿里云開(kāi)發(fā)者社區(qū)不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。具體規(guī)則請(qǐng)查看《阿里云開(kāi)發(fā)者社區(qū)用戶(hù)服務(wù)協(xié)議》和《阿里云開(kāi)發(fā)者社區(qū)知識(shí)產(chǎn)權(quán)保護(hù)指引》。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,填寫(xiě)侵權(quán)投訴表單進(jìn)行舉報(bào),一經(jīng)查實(shí),本社區(qū)將立刻刪除涉嫌侵權(quán)內(nèi)容。總結(jié)
以上是生活随笔為你收集整理的逸仙电商Seata企业级落地实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 针对《等保2.0》要求的云上最佳实践——
- 下一篇: Ask Me Anything #1 我