新聞中心
三年前,我寫了第一篇和分布式事務(wù)相關(guān)的文章再有人問你分布式事務(wù),把這篇扔給他,后面陸續(xù)也寫了一些和分布式事務(wù)相關(guān)的文章:

- 如何能在實戰(zhàn)中完成分布式事務(wù)
- 深度剖析一站式分布式事務(wù)方案Seata-Server
- 深度剖析一站式分布式事務(wù)方案Seata-Cient
- 解密分布式事務(wù)框架-Fescar
時隔三年,回看之前的文章,之前的確也有很多漏掉的一些知識,所以今天在這里再次和大家總結(jié)下分布式事務(wù)相關(guān)的東西。
事務(wù)
首先還是先說一下事務(wù)的定義吧,事務(wù)的英語是transaction,我們查找詞典可以發(fā)現(xiàn)這個單詞的中文解釋是交易,買賣等含義,所以我們可以知道事務(wù)一定和交易密不可分他們才能共享一個英文單詞,而交易的定義是什么呢?有句俗話說得好,一手交錢,一手交貨,那這個就是交易的規(guī)則,而這個同時也是事務(wù)的定義。那么事務(wù)的官方定義是什么呢?
事務(wù)是一系列操作的集合,這些操作要么都做,要么都不做,是一個不可分割的工作單位,是數(shù)據(jù)庫環(huán)境中的最小工作單元。
可以發(fā)現(xiàn)這個基本和我們一手交錢,一手交貨很像,的確在現(xiàn)實的開發(fā)環(huán)境中,在交易的業(yè)務(wù)中對事務(wù)的保證特別看重,而一些社交類的業(yè)務(wù),和資金關(guān)系不大的,比如點贊數(shù),評論數(shù),是不會對事務(wù)特別看重,這些應(yīng)該關(guān)注的是性能。
事務(wù)的類型
之前的文章都沒有介紹過事務(wù)的類型相關(guān),直到之前看了一篇文章,才知道事務(wù)是分了5種類型的,這里也介紹給大家:
扁平事務(wù)
我們?nèi)粘J褂玫幕径际潜馄绞聞?wù),以begin開始,然后以commit 或者 rollback結(jié)束.
begin;
do xxxx;
commit/rollback;
帶保存點的扁平事務(wù)
增加了SavePoint機制,內(nèi)存保存,如果數(shù)據(jù)庫宕機,savepoint將會丟失。
begin
insert into xxx
savepoint a
insert into yyy
rollback to a
這里的rollback to a只會回滾到保存點a這里,不會整個事務(wù)都回滾
鏈式事務(wù)
當我們提交事務(wù)后,相當于執(zhí)行了 COMMIT AND CHAIN,也就是開啟一個鏈式事務(wù),即當我們提交事務(wù)之后會開啟一個相同隔離級別的事務(wù)。如果回滾只會回滾當前節(jié)點。
通過下面sql語句可以再mysql里面查到當前是否開啟鏈式模式,以及如何開啟
select @@completion_type
set @@completion_type = 1 // 0無鏈式,
嵌套事務(wù)
- 嵌套事務(wù)可以是一棵樹,其中的葉節(jié)點可以使扁平事務(wù),也可以是嵌套事務(wù),但是都叫做子事務(wù)
- 某個節(jié)點回滾只影響當前節(jié)點下面所有的事務(wù)
- 子節(jié)點的提交是會根據(jù)父節(jié)點提交才會最后提交,也就說,所有事務(wù)的保存只能再最頂層提交,才會生效。
- mysql不支持嵌套事務(wù),Oracle支持
分布式事務(wù)
通常是一個在分布式環(huán)境下運行的扁平事務(wù),需要根據(jù)數(shù)據(jù)所在位置訪問網(wǎng)絡(luò)中的不同節(jié)點,一般來說對于分布式事務(wù),其同樣需要滿足單機 ACID 特性,要么都發(fā)生,要么都失效。但是實際實現(xiàn)的情況,可能遠比理想的更為復(fù)雜,所以通常會降低要求。
單機事務(wù)
上面講了五種類型的事務(wù),前4種其實都可以歸結(jié)為單機事務(wù),而單機事務(wù)也是后端程序員中經(jīng)常接觸到的,所以先簡單講講單機事務(wù)的核心關(guān)鍵點:
ACID
ACID是單機事務(wù)的四大特性,由這四個特性可以定義到底什么條件下才能算作事務(wù)。
- A:原子性——事務(wù)內(nèi)的操作要么全部提交,要么全部回滾。
- C:一致性——一個事務(wù)執(zhí)行之前和執(zhí)行之后數(shù)據(jù)庫都必須處于一致性狀態(tài)(數(shù)據(jù)庫的數(shù)據(jù)要么處于事務(wù)前的狀態(tài),要么處于事務(wù)后的狀態(tài))。
- I:隔離性—— 在并發(fā)環(huán)境中,當不同的事務(wù)同時操縱相同的數(shù)據(jù)時,每個事務(wù)都有各自的完整數(shù)據(jù)空間。
- D:持久性——事務(wù)成功結(jié)束,它對數(shù)據(jù)庫所做的更新就必須永久保存下來。即使發(fā)生系統(tǒng)崩潰,重新啟動數(shù)據(jù)庫系統(tǒng)后,數(shù)據(jù)庫還能恢復(fù)到事務(wù)成功結(jié)束時的狀態(tài)
實現(xiàn)關(guān)鍵
這里只講一下mysql的一些實現(xiàn)關(guān)鍵:
整體來說事務(wù)的ACID是通過InnoDB日志和鎖來保證:。
- 事務(wù)的隔離性是通過數(shù)據(jù)庫鎖的機制實現(xiàn)的
- 持久性通過redolog(重做日志)來實現(xiàn)。
- 原子性通過Undolog來實現(xiàn)。
- 一致性依靠三面上個特性來實現(xiàn)
redolog,undolog,binlog通常會容易搞混淆,這里簡單說一下
undolog:用于回滾和mvcc,可以理解他用于記錄之前版本的數(shù)據(jù)。
redolog:數(shù)據(jù)庫為了加快刷盤速度,采用了WAL的方式,也就是先順序預(yù)寫日志,然后再異步去刷盤,而redolog就是順序?qū)懭罩镜漠a(chǎn)物。
binlog:上面都是innodb引擎的日志,binlog是mysql-server的日志,用于主從同步等作用。
代碼使用
java程序員的話如果使用的是spring,那么使用本地事務(wù)會有兩種手段:
聲明式事務(wù)
其實就是直接加個注解,spring自己會做一個切面代理,然后使用事務(wù),這種方法使用得應(yīng)該也是最多的,因為他是最簡單的,但是說如果只想控制這個方法部分代碼進入事務(wù),或者調(diào)用同一個類的方法使用事務(wù),那就不太能使用這種方法。
編程式事務(wù)
編程式事務(wù),實現(xiàn)起來稍顯麻煩,需要自己手寫很多代碼,但是能解決上面說的那個問題。這種方式更加靈活多變。
分布式事務(wù)
為什么需要分布式事務(wù)
我們會發(fā)現(xiàn)分布式事務(wù)在最近幾年里提到的聲音越來越多,這是究竟為什么呢?在《架構(gòu)即未來》這本書中提到了AKF模型是軟件架構(gòu)擴展的基礎(chǔ)模型
如上圖,如果我們要對一個軟件進行擴展,那么需要以AKF模型為基礎(chǔ),從三個維度進行擴展。
- X軸:x軸的意思就是將以前的單機,變成集群,從臺機器,擴容成N臺。
- Y軸:以前的單體服務(wù)將所有業(yè)務(wù)代碼都寫在一個服務(wù)里面,而Y軸做的就是將這些業(yè)務(wù)模塊分開,拆分成微服務(wù)。
- Z軸:很多業(yè)務(wù)瓶頸在數(shù)據(jù)庫,通常我們可以做數(shù)據(jù)庫分庫分表,單元化等等
在Y軸中我們之前的單體服務(wù)被拆分成了多個服務(wù),那就有可能以前一個事務(wù)的內(nèi)容,可能出現(xiàn)在了多個服務(wù)中,比如一個訂單扣除積分和優(yōu)惠券,之前是一個服務(wù),如果拆分出來了有積分服務(wù)和優(yōu)惠券服務(wù),那么我們之前的本地事務(wù)就會失效,就需要引入分布式服務(wù)來保證一個訂單中的全部扣減都是成功的。
在Z軸中,如果我們做了分庫分表,也會破壞本地事務(wù),如果大家都是一個庫自然還能使用分庫分表,但如果操作了不同庫那么就無法保證事務(wù),那么也需要引入分布式事務(wù)。
而AKF又是現(xiàn)代軟件擴展的基礎(chǔ)模型,三者有其二可能都需要引入分布式事務(wù),所以,如果要對軟件進行擴展,那么分布式事務(wù)必不可少。
分布式的理論知識
CAP和BASE的理論知識應(yīng)該很多人都知道,但是我這里還是需要介紹一下,因為分布式事務(wù)離不開這兩個東西。
CAP
CAP定理,又被叫作布魯爾定理。CAP是分布式系統(tǒng)的入門理論。
- C (一致性):對某個指定的客戶端來說,讀操作能返回最新的寫操作。對于數(shù)據(jù)分布在不同節(jié)點上的數(shù)據(jù)上來說,如果在某個節(jié)點更新了數(shù)據(jù),那么在其他節(jié)點如果都能讀取到這個最新的數(shù)據(jù),那么就稱為強一致,如果有某個節(jié)點沒有讀取到,那就是分布式不一致。
- A (可用性):非故障的節(jié)點在合理的時間內(nèi)返回合理的響應(yīng)(不是錯誤和超時的響應(yīng))??捎眯缘膬蓚€關(guān)鍵一個是合理的時間,一個是合理的響應(yīng)。合理的時間指的是請求不能無限被阻塞,應(yīng)該在合理的時間給出返回。合理的響應(yīng)指的是系統(tǒng)應(yīng)該明確返回結(jié)果并且結(jié)果是正確的,這里的正確指的是比如應(yīng)該返回50,而不是返回40。
- P (分區(qū)容錯性):當出現(xiàn)網(wǎng)絡(luò)分區(qū)后,系統(tǒng)能夠繼續(xù)工作。打個比方,這里個集群有多臺機器,有臺機器網(wǎng)絡(luò)出現(xiàn)了問題,但是這個集群仍然可以正常工作。
BASE
BASE 是 Basically Available(基本可用)、Soft state(軟狀態(tài))和 Eventually consistent (最終一致性)三個短語的縮寫。是對CAP中AP的一個擴展。
1.基本可用:分布式系統(tǒng)在出現(xiàn)故障時,允許損失部分可用功能,保證核心功能可用。
2.軟狀態(tài):允許系統(tǒng)中存在中間狀態(tài),這個狀態(tài)不影響系統(tǒng)可用性,這里指的是CAP中的不一致。
3.最終一致:最終一致是指經(jīng)過一段時間后,所有節(jié)點數(shù)據(jù)都將會達到一致。
BASE解決了CAP中理論沒有網(wǎng)絡(luò)延遲,在BASE中用軟狀態(tài)和最終一致,保證了延遲后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的強一致性模型,而是通過犧牲強一致性來獲得可用性,并允許數(shù)據(jù)在一段時間內(nèi)是不一致的,但最終達到一致狀態(tài)。
分布式事務(wù)解決方案
剛性事務(wù)
剛性事務(wù)追求的是強一致性事務(wù)。
DTP/XA
DTP的XA規(guī)范(全稱為Distributed Transaction Processing The XA Specification)的制定者是X/Open,即現(xiàn)在的Open Group。
熟悉數(shù)據(jù)庫自帶的分布式事務(wù)支持的同學(xué),其實就知道這個就是XA,
這里的AP你可以理解成是我們自己的業(yè)務(wù)服務(wù),RM則是我們使用的數(shù)據(jù)庫通常都是使用mysql而mysql也自帶支持XA,TM可以是一個單獨的服務(wù),也可以是我們自己的業(yè)務(wù)服務(wù)承擔(dān),他的作用是做事務(wù)管理。如果是用mysql的話,使用XA有如下代碼:
XA BEGIN '123';
insert into xxx;
XA END '123'; // 鏈接斷開會失去這次事務(wù)
XA PREPARE '123'; // 二階段準備會持久化
XA COMMIT '123'; //prepare全部成功/或者有一個失敗就回滾
當然XA其實有很多缺點:
1.數(shù)據(jù)鎖定:數(shù)據(jù)在整個事務(wù)處理過程結(jié)束前,都被鎖定,讀寫都按隔離級別的定義約束起來。
2.協(xié)議阻塞:XA prepare 后,分支事務(wù)進入阻塞階段,收到 XA commit 或 XA rollback 前必須阻塞等待。
3.性能差:性能的損耗主要來自兩個方面:一方面,事務(wù)協(xié)調(diào)過程,增加單個事務(wù)的 RT;另一方面,并發(fā)事務(wù)數(shù)據(jù)的鎖沖突,降低吞吐。
柔性事務(wù)
因為剛性事務(wù)的實現(xiàn)成本較大,對于現(xiàn)在互聯(lián)網(wǎng)的業(yè)務(wù)來說很多不愿意承受這么大的成本性能損失,愿意犧牲一定的一致性來保證性能,所以我們這里叫做柔性事務(wù)。
消息最終一致
適用于很多異步任務(wù),在我們的場景中,比如異步審核筆記,異步發(fā)放積分(適用于加法的業(yè)務(wù))。而消息最終一致也有兩種實現(xiàn)方法。
消息表
qmq實現(xiàn)事務(wù)消息的方法是利用的消息表。首先我們需要有一張這樣的消息表,用來和我們業(yè)務(wù)在同一個事務(wù)里面一起保存。
CREATE TABLE `msg_queue` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`topic` varchar(64) NOT NULL,
`nameServer` varchar(64) NOT NULL,
`status` smallint(6) NOT NULL DEFAULT '0' COMMENT '消息狀態(tài)',
`error` int unsigned NOT NULL DEFAULT '0' COMMENT '錯誤次數(shù)',
`create_time` datetime NOT NULL COMMENT '創(chuàng)建時間',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='記錄業(yè)務(wù)系統(tǒng)消息';
從上面圖上可以看出,我們會有單獨的task在不斷掃描我們的消息表,如果消息表沒有被刪除掉,代表之前沒有發(fā)送成功,那么我們需要做發(fā)送,這里需要說的是,我們一定要保證這個消息是冪等的,因為這里的發(fā)送完之后刪除消息,并不能保證數(shù)據(jù)是一致的,有可能一個消息會發(fā)送多次,最后才能被刪掉。
使用的代碼如下:
@Transactional
public void pay(Order order){
saveOrder(order);
messageProducer.sendMessage(buildMessage(order)); //要寫在最后
}
rocketmq事務(wù)消息
rockemq的事務(wù)消息如圖上所示分為四個階段:
- 第一階段:先發(fā)送一個prepare消息,會獲取到一個prepare的消息id
- 第二階段:執(zhí)行本地事務(wù),如果執(zhí)行成功,則發(fā)送commit/失敗則rollback。
- 第三階段:rocketmq-server根據(jù)消息結(jié)果,如果成功就投遞給consumer,不成功則把消息刪除掉。
- 第四階段: 如果二階段沒有上報消息結(jié)果,那就需要進行回查。
最大努力通知
適合于開放平臺,外部的第三方系統(tǒng)想要保證最終一致。比如微信,支付寶的開放平臺。下面我貼一個微信支付平臺的執(zhí)行流程:
從上面可以看到,最大努力通知需要保證兩點:
- 有限次數(shù)的重試,一般重試策略采用指數(shù)退避
- 需要提供查詢接口,來防止通知失敗。
TCC
做支付的同學(xué)比較常用的,雖然是柔性事物,但是目標是有剛性的效果。隔離性比較強。
舉個例子:有積分服務(wù),券服務(wù),余額服務(wù),如果用戶一次訂單想同時扣減這三個怎么能保證。用其他的模式可以嗎?
消息最終一致和最大努力通知,都不太適合,無法保證隔離型,用戶重復(fù)使用資產(chǎn)。XA性能差。
所以這里我們選擇使用了TCC,分三個方法:
- try: 鎖定,通常用一個字段或者記錄。(演化成saga直接try commit合并)
- commit: 提交資源
- cancel:釋放資源
SAGA
Saga是30年前一篇數(shù)據(jù)庫倫理提到的一個概念。其核心思想是將長事務(wù)拆分為多個本地短事務(wù),由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。
- 適用于無法提供TCC接口(遺留系統(tǒng),外部系統(tǒng)),一般來說提供提交 和 回滾接口即可 ,這里的可以看做業(yè)務(wù)上的接口,生成訂單 對應(yīng) 的刪除訂單就是回滾,不需要單獨命名回滾接口。
- 不看隔離性,一階段就生效
- 想異步執(zhí)行
- 想支持正向重試(tcc,try 為什么不能正向重試,資源一直被業(yè)務(wù)隔離,需要釋放隔離性)
SEATA
當然上面介紹了很多種分布式事務(wù),有同學(xué)會想,說了這么多但是我該怎么實現(xiàn)呢?那我在這里推薦使用seata,seata 是一款開源的分布式事務(wù)解決方案,致力于在微服務(wù)架構(gòu)下提供高性能和簡單易用的分布式事務(wù)服務(wù)。為用戶提供了 AT、TCC、SAGA 和 XA 事務(wù)模式。
有興趣的可以訪問seata官網(wǎng):https://seata.io/zh-cn 。我這里就不具體介紹了,或者看我之前的文章也有很多介紹。
最后
時隔這么久,再次寫了一下關(guān)于分布式事務(wù)相關(guān)的,這次算對之前的是補充,當然也算是對分布式事務(wù)的總結(jié)。希望大家能在自己的業(yè)務(wù)中能找到合適自己的分布式事務(wù)的方法。
當前標題:再談分布式事務(wù),你了解多少?
瀏覽地址:http://www.dlmjj.cn/article/djjdpid.html


咨詢
建站咨詢
