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