分散式事務分散式鎖的常用解決方式
一、分散式中的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是執行提交操作還是回滾操作;具體描述如下:
應用程式向事務管理器(TM)提交請求,發起方分散式事務;
一階段,事務管理器(TM)聯絡所有資源管理器(RM),通知它們執行準備操作;
資源管理器(RM)返回準備成功,或者失敗的訊息給TM(響應超時算作失敗);
二階段,如果所有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、事務組執行操作後,釋放所有資源