分布式事務和分布式鎖


一、分布式中的CAP原則

1.1 CAP的概念

CAP原則指的是在一個分布式系統中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性),最多只能滿足兩個,三者不能兼得

  • Consistency(一致性)

指的是對於每一次的讀取操作,要么都能夠讀取到最新的寫入數據,要么就錯誤

  • Availability(可用性)

指的是對於每一次請求,都能夠得到一個及時的、正確的響應,但是不保證請求的結果是基於最新寫入的數據,不出現用戶操作失敗或者訪問超時等用戶體驗不好的情況,

  • Partition tolerance(分區容錯性)

指的是節點之間的網絡問題,即使一些消息對多包或者延遲,整個系統能繼續提供服務

分區容錯性能夠讓你的系統在部分斷網的情況下仍然可以可以完全正常的運轉。實現分區容錯的一種常見方式就是服務切分成不同的“分區”,甚至可以處於不同的網絡問題上,這樣做的優勢是,如果某個網段斷線了,並不會把整個系統拖垮

1.2 為什么會有CAP原則?

​ 現如今,對於多數大型互聯網應用的場景,主機眾多、部署分散,而且現在的集群規模越來越大,節點只會越來越多,所以節點故障、網絡故障是常態,因此分區容錯性也就成為了一個分布式系統必然要面對的問題。

1.3 zookeeper中是保證CP原則的

​ zookeeper會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩余節點會重新進行leader選舉。問題在於,選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk集群都是不可用的,這就導致在選舉期間注冊服務癱瘓。在雲部署的環境下,因網絡問題使得zk集群失 去master節點是較大概率會發生的事,雖然服務能夠最終恢復,但是漫長的選舉時間導致的注冊長期不可用是不能容忍的。

1.4 zookeeper的實現原理

zookepper服務正常啟動后,所有的zookepper客戶端都會監聽某一個節點,當這個節點內容發生個改變后,zookeeper的服務會給所有的zookeeper客戶端發送一個事件通知,客戶端收到這個事件通知后,會拉取最新的數據

1.5 Eureka是保證AP的

​ Eureka看明白了這一點,因此在設計時就保證可用性。Eureka各個節點都是平等的,幾個節點掛掉不會影響其他節點正常的工作,剩余的節點依舊可以提供注冊和查詢服務。而Eureka的客戶端在向某個Eureka服務進行注冊時,如果發現連接失敗,則會自動切換至其他節點,只要有一台Eureka服務存在,就還能保證服務的注冊可用(保證可用性),只不過查詢到的信息不是最新的(不保證強一致性)

​ 除此之外,Eureka還有一種自我保護機制,如果在十五分鍾內超過百分之八十五的節點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現了故障,此時會有以下幾種情形:

1、Eureka不會從出冊表移除因長時間沒有收到心跳而移除的服務

2、Eureka仍然能夠接收新服務的注冊和查詢請求,但是不會被同步到其他節點上,即保證當前節點依然可用

3、當網絡穩定時,當前實例的注冊信息會被同步到其他節點中

​ Eureka 自我保護機制是為了防止誤殺服務而提供的一個機制。當個別客戶端出現心跳失聯時,則認為是客戶端的問題,剔除掉客戶端;當 Eureka 捕獲到大量的心跳失敗時,則認為可能是網絡問題,進入自我保護機制;當客戶端心跳恢復時,Eureka 會自動退出自我保護機制。

如果在保護期內剛好這個服務提供者非正常下線了,此時服務消費者就會拿到一個無效的服務實例,即會調用失敗。對於這個問題需要服務消費者端要有一些容錯機制,如重試,斷路器等。

1.6 Eureka的實現原理

1、Eureka服務正常啟動,如果存在集群的話就要互相同步

2、Eureka客戶端啟動的時候,會根據配置的地址,將該服務注冊到Eureka服務中,

3、Eureka客戶端會每隔30s發送一個心跳給Eureka服務

4、Eureka服務在90s之內沒有收到Eureka客戶端的心跳,會認為客戶端出現故障,然后從服務列表中移除,

5、在一段時間內,Eureka服務端統計到有大量的(85%)Eureka客戶端沒有發送心跳Eureka服務會認為此時,自己出現了網絡故障,就會觸發自我保護機制,不會再移除eureka客戶端。當前不會把數據同步給其他的Eureka服務,但是對外還是提供服務的

6、如果網絡恢復正常,自我保護機制關閉,接着將數據同步到其他的Eureka服務器

7、Eureka客戶端要調用其他服務,需要先到Eureka服務器中拉取其他服務的信息,然后再緩存到本地,再根據客戶端的負載均衡策略進行負載均衡

8、Eureka客戶端會在一段時間內從Eureka服務端拉取最新的數據,更新本地的緩存數據。

9、Eureka客戶端關閉后,Eureka就不會再發送心跳,Eureka服務就從自己的列表中移除

1.7 Zookepper和Eureka的區別:

1、所屬的組織不同,zookeeper是apache組織下的,而Eureka是netfix的

2、zookeeper選擇的是CP原則,即一致性,而eureka選擇的是AP,即可用性

3、zookeeper的集群中,zookeeper會存在三個角色,群首(leader),追隨者(follower),觀察者(observer),而Eureka中是不存在角色的,每個節點都是平等的

4、實現原理不一致

二、分布式鎖

2.1 引言

由於傳統的鎖是基於Tomcat服務器內部的,搭建了集群之后,導致鎖失效,使用分布式鎖來處理。

2.2 回顧傳統單體架構的鎖

​ 我們遇到多個線程操作同一資源的時候,我們往往會采用同步代碼塊synchronized的方式來處理,但是同步代碼塊中那個鎖使用this(對象鎖)和使用類名.class的又有什么區別呢,下面我們同過代碼來試驗

使用this的方式

public class SynLockTest { @Test public void test1() throws IOException { //創建連個對象 User user1 = new User("張三"); User user2 = new User("李四"); UserThread userThread1 = new UserThread(user1); UserThread userThread2 = new UserThread(user2); //2、啟動線程 for (int i = 0; i < 10; i++) { new Thread(userThread1).start(); new Thread(userThread2).start(); } System.in.read(); } } //自定義一個線程類 class UserThread implements Runnable{ private User user; public UserThread(User user){ this.user = user; } @Override public void run() { try { user.info(); } catch (InterruptedException e) { e.printStackTrace(); } } } class User{ public String username; public User(String username){ this.username = username; } public void info() throws InterruptedException { synchronized (this){ // System.out.println("this="+this); Thread.sleep(1000); System.out.println("當前線程的名字為:"+Thread.currentThread().getName()+"____"+username); } } } 

發現結果是兩條數據一起打印的,說明當前的鎖並沒有起作用

修改代碼,使用類鎖

public void info() throws InterruptedException { synchronized (System.class){ // System.out.println("this="+this); Thread.sleep(1000); System.out.println("當前線程的名字為:"+Thread.currentThread().getName()+"____"+username); } } 

發現數據是一行一行輸出的,說明同步代碼塊中的鎖起到了作用

2.3 雙重鎖機制

@Override public List<User> getUserList() { List<User> userList = null; userList= (List<User>) redisTemplate.opsForValue().get("userList"); if(userList == null){ synchronized (this){ if(userList == null){ System.out.println("查詢數據庫"); userList = userDao.select(null); // 進行緩存重建 redisTemplate.opsForValue().set("userList",userList); redisTemplate.expire("userList",5, TimeUnit.SECONDS); // 5s后失效 } } } return userList; } 

2.4 分布式鎖實現原理

這里將演示兩種分布式鎖解決方式,分別是使用Redis和Zookeeper解決

實現原理:

​ reids:redis實現的原理是使用reids的setnx命令,當添加成功時認為拿到鎖,邏輯業務執行完畢后刪除key,認為是釋放鎖

​ Zookeeper:Zookeeper實現分布式鎖的原理就是使用臨時有序節點的方式,客戶端在指定節點下創建臨時有序節點時,如果說序號是最小的就獲取了鎖資源,如果說當前節點不是最小的,監聽比自己小一號的節點,如果這個小一號節點被刪除了,當前節點再次判斷自己的是否為最小的節點,如果是就拿到鎖資源

# 設置值,如果key已存在不添加且返回0,不存在添加且返回1 setnx key value 

2.5 基於reids高並發搶票案例

1、導入依賴

<!--zookeeper的高級API,內部已經包含了zookeeper依賴--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency> 

2、將zookeeper客戶端注入到spring容器中,當然也可以不注入,自己創建

@Configuration public class ZookeeperClient { //將zookeeper的高級客戶端注入到容器 @Bean public CuratorFramework cf() throws Exception{ RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2); CuratorFramework cf = CuratorFrameworkFactory.builder(). connectString("192.168.40.100:2181") .retryPolicy(retryPolicy) .build(); cf.start(); return cf; } } 

3、編寫秒殺業務

/** * 測試秒殺的業務controller 使用的是zookeeper * @param * @return */ @RestController public class SeckillControllerZookeeper { //模擬數據庫中的商品數據 private Map<String,Integer> stockMap = new HashMap<>(); //模擬數據庫中的訂單數據 private Map<String,Integer> orderMap = new HashMap<>(); //初始化數據 @PostConstruct public void init(){ stockMap.put("手機",1000);//初始化手機數量數據庫中的數據 orderMap.put("手機",0);//初始化數據庫中的數據 } private CuratorFramework cf; @Autowired RedisLock redisLock; //秒殺 @RequestMapping("/seckillGoods") public String seckillGoods(String gname) throws Exception { //獲取鎖 boolean lock = redisLock.getLock(gname, "1", 1L); if(lock){ //1、根據商品名稱獲取庫存 Integer stock = stockMap.get(gname); //2、判斷 if(stock <= 0){ return "商品已經被搶光了,請等待下一輪秒殺"; } //3、庫存-1 stockMap.put(gname,stock-1); Thread.sleep(200);//模擬修改數據庫,需要耗時 //4、訂單需要加1 Integer orderCount = orderMap.get(gname); orderMap.put(gname,orderCount+1); Thread.sleep(200);//模擬修改數據庫,需要耗時 //釋放鎖 redisLock.unLock(gname); //5、響應用戶 return "搶購【"+gname+"】成功,商品數量還剩【"+stockMap.get(gname)+"】,訂單量為【"+orderMap.get(gname)+"】"; }else { return "當前訪問人數過多~"; } } } 

4、使用高並發測壓工具進行測試

2.6 基於reids高並發搶票案例

1、導入依賴

<!-- springBoot整合reids的依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 

2、編寫獲取鎖和釋放鎖的工具類

/** * redis處理分布式鎖的原理 * 拿到鎖:redis使用setnx命令,如果存儲成功就拿到鎖 * 釋放鎖:刪除對應的數據 * @param * @return */ @Component public class RedisLock { @Autowired private StringRedisTemplate template; /** * 獲取資源的方法,其原理是redis的是使用redis的setnx命令,如果存儲相同的值會返回一個標識 * @param key 存儲的key * @param val 存儲的值 * @param ex 超時時間,謹防死鎖的出現 * @return */ public boolean getLock(String key,String val,Long ex){ return template.opsForValue().setIfAbsent(key,val,ex, TimeUnit.SECONDS); } /** * 釋放鎖,其原理是刪除redis中的數據, */ public void unLock(String key){ template.delete(key); } } 

3、編寫秒殺業務

/** * 測試秒殺的業務controller 使用的是zookeeper * @param * @return */ @RestController public class SeckillControllerZookeeper { //模擬數據庫中的商品數據 private Map<String,Integer> stockMap = new HashMap<>(); //模擬數據庫中的訂單數據 private Map<String,Integer> orderMap = new HashMap<>(); //初始化數據 @PostConstruct public void init(){ stockMap.put("手機",1000);//初始化手機數量數據庫中的數據 orderMap.put("手機",0);//初始化數據庫中的數據 } @Autowired private CuratorFramework cf; @Autowired RedisLock redisLock; //秒殺 @RequestMapping("/seckillGoods") public String seckillGoods(String gname) throws Exception { //獲取鎖 boolean lock = redisLock.getLock(gname, "1", 1L); if(lock){ //1、根據商品名稱獲取庫存 Integer stock = stockMap.get(gname); //2、判斷 if(stock <= 0){ return "商品已經被搶光了,請等待下一輪秒殺"; } //3、庫存-1 stockMap.put(gname,stock-1); Thread.sleep(200);//模擬修改數據庫,需要耗時 //4、訂單需要加1 Integer orderCount = orderMap.get(gname); orderMap.put(gname,orderCount+1); Thread.sleep(200);//模擬修改數據庫,需要耗時 //釋放鎖 redisLock.unLock(gname); //5、響應用戶 return "搶購【"+gname+"】成功,商品數量還剩【"+stockMap.get(gname)+"】,訂單量為【"+orderMap.get(gname)+"】"; }else { return "當前訪問人數過多~"; } } } 

三、分布式事務

3.1 什么是分布式事務

分布式事務就是指事務的資源分別位於不同的分布式系統的不同節點之上的事務;

3.2 分布式事務產生的原因

3.2.1 數據庫分庫分表

在單庫單表場景下,當業務數據量達到單庫單表的極限時,就需要考慮分庫分表,將之前的單庫單表拆分成多庫多表;分庫分表之后,原來在單個數據庫上的事務操作,可能就變成跨多個數據庫的操作,此時就需要使用分布式事務;

3.2.3 業務服務化

​ 業務服務化即業務按照面向服務(SOA)的架構拆分整個網站系統;

​ 比如互聯網金融網站SOA拆分,分離出交易系統、賬務系統、清算系統等,交易系統負責交易管理和記錄交易明細,賬務系統負責維護用戶余額,所有的業務操作都以服務的方式對外發布;

​ 一筆金融交易操作需要同時記錄交易明細和完成用戶余額的轉賬,此時需要分別調用交易系統的交易明細服務和賬務系統的用戶余額服務,這種跨應用、跨服務的操作需要使用分布式事務才能保證金融數據的一致性;

3.4 Base理論

在我們之前的CAP原則 C:一致性, A:可用性,P:分區容錯性,在分布式環境中,三者只能取其二

  • Eureka:AP,保證了可用性,舍棄了一致性。
  • Zookeeper,Redis:CP,每一個節點必須能夠找到Master才能對外提供服務,舍棄了可用性。

一致性:

強一致性:任何時候查詢任何節點都要保證數據是最新的狀態,如zookeeper

最終一致性:允許任何時間查詢的數據不一致,但是要保證最終數據一致,如reids的主從復制

BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)三個短語的簡寫,BASE是對CAP中一致性和可用性權衡的結果,其來源於對大規模互聯網系統分布式實踐的結論,是基於CAP定理逐步演化而來的,其核心思想是即使無法做到強一致性(Strong consistency),但每個應用都可以根據自身的業務特點,采用適當的方式來使系統達到最終一致性(Eventual consistency)

基本可用

基本可用是指分布式系統在出現不可預知故障的時候,允許損失部分可用性——但請注意,這絕不等價於系統不可用,以下兩個就是“基本可用”的典型例子。

  • 響應時間上的損失:正常情況下,一個在線搜索引擎需要0.5秒內返回給用戶相應的查詢結果,但由於出現異常(比如系統部分機房發生斷電或斷網故障),查詢結果的響應時間增加到了1~2秒。
  • 功能上的損失:正常情況下,在一個電子商務網站上進行購物,消費者幾乎能夠順利地完成每一筆訂單,但是在一些節日大促購物高峰的時候,由於消費者的購物行為激增,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面。

弱狀態也稱為軟狀態,和硬狀態相對,是指允許系統中的數據存在中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性,即允許系統在不同節點的數據副本之間進行數據聽不的過程存在延時。

最終一致性

最終一致性強調的是系統中所有的數據副本,在經過一段時間的同步后,最終能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終數據能夠達到一致,而不需要實時保證系統數據的強一致性

亞馬遜首席技術官Werner Vogels在於2008年發表的一篇文章中對最終一致性進行了非常詳細的介紹。他認為最終一致性時一種特殊的弱一致性:系統能夠保證在沒有其他新的更新操作的情況下,數據最終一定能夠達到一致的狀態,因此所有客戶端對系統的數據訪問都能夠胡渠道最新的值。同時,在沒有發生故障的前提下,數據達到一致狀態的時間延遲,取決於網絡延遲,系統負載和數據復制方案設計等因素。

在實際工程實踐中,最終一致性存在以下五類主要變種。

因果一致性:

​ 因果一致性是指,如果進程A在更新完某個數據項后通知了進程B,那么進程B之后對該數據項的訪問都應該能夠獲取到進程A更新后的最新值,並且如果進程B要對該數據項進行更新操作的話,務必基於進程A更新后的最新值,即不能發生丟失更新情況。與此同時,與進程A無因果關系的進程C的數據訪問則沒有這樣的限制。

讀己之所寫:

​ 讀己之所寫是指,進程A更新一個數據項之后,它自己總是能夠訪問到更新過的最新值,而不會看到舊值。也就是說,對於單個數據獲取者而言,其讀取到的數據一定不會比自己上次寫入的值舊。因此,讀己之所寫也可以看作是一種特殊的因果一致性。

會話一致性:

​ 會話一致性將對系統數據的訪問過程框定在了一個會話當中:系統能保證在同一個有效的會話中實現“讀己之所寫”的一致性,也就是說,執行更新操作之后,客戶端能夠在同一個會話中始終讀取到該數據項的最新值。

單調讀一致性:

​ 單調讀一致性是指如果一個進程從系統中讀取出一個數據項的某個值后,那么系統對於該進程后續的任何數據訪問都不應該返回更舊的值。

單調寫一致性:

​ 單調寫一致性是指,一個系統需要能夠保證來自同一個進程的寫操作被順序地執行。

以上就是最終一致性的五類常見的變種,在時間系統實踐中,可以將其中的若干個變種互相結合起來,以構建一個具有最終一致性的分布式系統。事實上,可以將其中的若干個變種相互結合起來,以構建一個具有最終一致性特性的分布式系統。事實上,最終一致性並不是只有那些大型分布式系統才設計的特性,許多現代的關系型數據庫都采用了最終一致性模型。在現代關系型數據庫中,大多都會采用同步和異步方式來實現主備數據復制技術。在同步方式中,數據的復制國恥鞥通常是更新事務的一部分,因此在事務完成后,主備數據庫的數據就會達到一致。而在異步方式中,備庫的更新往往存在延時,這取決於事務日志在主備數據庫之間傳輸的時間長短,如果傳輸時間過長或者甚至在日志傳輸過程中出現異常導致無法及時將事務應用到備庫上,那么狠顯然,從備庫中讀取的的數據將是舊的,因此就出現了不一致的情況。當然,無論是采用多次重試還是認為數據訂正,關系型數據庫還是能搞保證最終數據達到一致——這就是系統提供最終一致性保證的經典案例。

總的來說,BASE理論面向的是大型高可用可擴展的分布式系統,和傳統事務的ACID特性使相反的,它完全不同於ACID的強一致性模型,而是提出通過犧牲強一致性來獲得可用性,並允許數據在一段時間內是不一致的,但最終達到一致狀態。但同時,在實際的分布式場景中,不同業務單元和組件對數據一致性的要求是不同的,因此在具體的分布式系統架構設計過程中,ACID特性與BASE理論往往又會結合在一起使用。

其他關於Base的資料

  • 基於CAP理論演化而來的,是對CAP定理中一致性和可用性的一個權衡結果。
  • 核心思想:我們無法做到強一致性,但是每一個應用都可以根據自身的業務特點,采用一些適當的方式來權衡,最終達到一致性。
  • 基本可用:分布式系統在出現故障時,允許損失部分可用功能,保證核心功能可用。如,電商網站交易付款出現問題了,商品依然可以正常瀏覽。
  • 軟狀態:由於不要求強一致性,所以BASE允許系統中存在中間狀態(也叫軟狀態),這個狀態不影響系統可用性,如訂單的"支付中"、“數據同步中”等狀態,待數據最終一致后狀態改為“成功”狀態。
  • 最終一致:最終一致是指經過一段時間后,所有節點數據都將會達到一致。如訂單的"支付中"狀態,最終會變為“支付成功”或者"支付失敗",使訂單狀態與實際交易結果達成一致,但需要一定時間的延遲、等待。

四、分布式事務的解決方案

4.1 2pc兩段提交

兩階段提交協議(Two Phase Commitment Protocol)是分布式事務的基礎協議。

在此協議中,一個事務協調器(TM, transaction manager)協調多個資源管理器(RM, resource manager)的活動;在一階段所有資源管理器(RM)向事務管理器(TM)匯報自身活動狀態,在第二階段事務管理器(TM)根據各資源管理器(RM)匯報的狀態,來決定各RM是執行提交操作還是回滾操作;具體描述如下:

  1. 應用程序向事務管理器(TM)提交請求,發起方分布式事務;

  2. 一階段,事務管理器(TM)聯絡所有資源管理器(RM),通知它們執行准備操作;

  3. 資源管理器(RM)返回准備成功,或者失敗的消息給TM(響應超時算作失敗);

  4. 二階段,如果所有RM均准備成功,TM會通知所有RM執行提交;如果任一RM准備失敗,TM會通知所有RM回滾;

通過事務管理器2階段協調資源管理器,使所有資源管理器的狀態最終都是一致的,要么全部提交,要么全部回滾。

4.1.1 2PC兩段提交存在的問題

問題1:執行的性能是很低的。一般是傳統事務的10倍以上。

問題2:TransactionManager是沒有超時時間的。

問題3:TransactionManager存在單點故障的問題

4.2 3PC三段提交

三段提交在二段提交的基礎上,引入了超時時間機制,並且在二段提交的基礎上,又多了一個步驟,在提交事務之前,再詢問一下,數據庫的日志信息,是否已經完善。

4.2.1 undo

undo日志用於存放數據修改被修改前的值,假設修改 tba 表中 id=2的行數據,把Name=’B’ 修改為Name = ‘B2’ ,那么undo日志就會用來存放Name=’B’的記錄,如果這個修改出現異常,可以使用undo日志來實現回滾操作,保證事務的一致性 。即是記錄修改前的內容,方便事務的回滾

4.2.2 redo

redo是當數據庫對數據做修改的時候,需要把數據頁從磁盤讀到buffer pool中,然后在buffer pool中進行修改,那么這個時候buffer pool中的數據頁就與磁盤上的數據頁內容不一致,如果這個時候發生非正常的DB服務重啟,數據並沒有同步到磁盤文件中,也就是會發生數據丟失,如果這個時候,能夠在有一個文件,當buffer pool 中的data page變更結束后,把相應修改記錄記錄到這個文件,那么當DB服務發生宕機的情況,恢復DB的時候,也可以根據這個文件的記錄內容,重新應用到磁盤文件,數據保持一致。即記錄事務修改以后的內容

4.2.3 Buffer Pool

應用系統分層架構,為了加速數據訪問,會把最常訪問的數據,放在緩存(cache)里,避免每次都去訪問數據庫。
操作系統,會有緩沖池(buffer pool)機制,避免每次訪問磁盤,以加速數據的訪問。
MySQL作為一個存儲系統,同樣具有緩沖池(buffer pool)機制,以避免每次查詢數據都進行磁盤IO。

緩沖池(buffer pool)是一種常見的降低磁盤訪問的機制;
緩沖池通常以頁(page)為單位緩存數據;

4.3 TCC機制

TCC(Try,Confirm,Cancel),和你的業務代碼切合在一起。

  • Try:嘗試去預執行具體業務代碼。 下單訂ing。。。

  • try成功了:Confirm:再次執行Confirm的代碼。

  • try失敗了:Cancel:再次執行Cancel的代碼。

TCC成功情況
TCC失敗情況

TCC分為三個階段 :

​ Try階段是做業務檢查(一致性)及資源預留(隔離),此階段僅是一個初步操作,它和后續的Confirm一起才能真正構成一個完整的業務邏輯。

​ Confirm階段是做確認提交,Try階段所有分支事務執行成功后開始執行Confirm。通常情況下,采用TCC則認為Confirm階段是不會出錯的。即 :只要Try成功,Confirm一定成功。若Confirm階段真的出錯了,需引入重試機制或人工處理。

​ Cancel階段是在業務執行錯誤需要回滾的狀態下執行分支事務的業務取消,預留資源釋放。通常情況下,采用TCC則認為Cancel階段也是一定成功的。若Cancel階段真的出錯了,需引入重試機制或人工處理。

4.4 MQ分布式事務

RabbitMQ在發送消息時,confirm機制,可以保證消息發送到MQ服務中,消費者有手動ack機制,保證消費到MQ中的消息。

MQ分布式事務

總結

1、2pc a)分為兩個階段 b)准備階段:開啟事務,執行的sql c)提交階段:事務的參與者要么回滾事務,要么提交事務 2、3PC a)分為3ge階段 b)准備階段:開啟事務,執行的sql c)詢問階段:是否寫入undo,redo日志文件成功 a)undo:記錄修改的之前的日志,方便事務回滾 b)redo:記錄修改的后的內容,DB宕機后方便恢復數據 b)Buffer pool:減少和磁盤的交互 d)提交階段:事務的參與者要么回滾事務,要么提交事務 3、TCC a)嘗試執行:開啟事務,執行sql b)確認執行:刪除日志 c)取消:讀取日志,回滾數據 4、MQ a)處理的過程 a)服務1操作完給MQ發送一個消息 b)服務2負責監聽MQ, c)由於MQ自帶消息確認機制,可以包保證消息肯定能發出或者被消費。 b)如何保證操作數據庫和發送消息隊列是原子性的操作 a)操作數據庫 b)寫入表中一個消息 c)定時任務掃描這個表,再發送消息給隊列中 

4.5 TX-LCN實現分布式事務

基於三段提交和TCC實現的

TX-LCN分布式事務框架,LCN並不生產事務,LCN只是本地事務的協調工,LCN是一個高性能的分布式事務框架,兼容dubbo、springcloud框架,支持RPC框架拓展,支持各種ORM框架、NoSQL、負載均衡、事務補償.

特性一覽
1、一致性,通過TxManager協調控制與事務補償機制確保數據一致性
2、易用性,僅需要在業務方法上添加@TxTransaction注解即可
3、高可用,項目模塊不僅可高可用部署,事務協調器也可集群化部署
4、擴展性,支持各種RPC框架擴展,支持通訊協議與事務模式擴展

實現原理圖

執行過程:

1、服務發起者會在事務協調者創建事務組,並且將這個事務加入到事務組,並生成一個事務組id

2、將這個事務組id放到了上下文中,其他事務參與者一直添加到事務組中,直到有結束標識出現

3、事務協調者向所有的事務參與者發送詢問,是否能夠進行提交,如果是全部返回可以提交,那么進行提交,否則只要有一個回滾的標記則整個事務組回滾

4、事務組執行操作后,釋放所有資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM