本地事務(wù)
對于一組操作铆隘, 要么全部成功,要么全部失敗项炼。
特點(diǎn):
ACID 原子性 一致性 隔離性 持久性
原子性:通過undolog日志記錄修改之前的數(shù)據(jù)但绕,如果需要回滾根據(jù)undolog回滾
一致性:滿足一些預(yù)定的規(guī)則(外鍵約束)
隔離性:不同事務(wù)之間互不影響 (基于鎖實(shí)現(xiàn))
持久性:通過redolog實(shí)現(xiàn),所有更改的數(shù)據(jù)都會先備份到redolog里面扶供,所有操作完以后提交事務(wù)筛圆,此時將redolog中的數(shù)據(jù)刷新到磁盤
為什么要用redolog?
因?yàn)樗琼樞騃O椿浓,效率高
spring事務(wù)的7種傳播行為
@Transactional(propagation=Propagation.REQUIRED)
PROPAGATION_REQUIRED 如果當(dāng)前沒有事務(wù)太援,就新建一個事務(wù),如果已經(jīng)存在一個事務(wù)中轰绵,加入到這個事務(wù)中粉寞。這是最常見的選擇尼荆。
PROPAGATION_SUPPORTS 支持當(dāng)前事務(wù)左腔,如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行捅儒。
PROPAGATION_MANDATORY 使用當(dāng)前的事務(wù)液样,如果當(dāng)前沒有事務(wù),就拋出異常巧还。
PROPAGATION_REQUIRES_NEW 新建事務(wù)鞭莽,如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起麸祷。
PROPAGATION_NOT_SUPPORTED 以非事務(wù)方式執(zhí)行操作澎怒,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起阶牍。
PROPAGATION_NEVER 以非事務(wù)方式執(zhí)行喷面,如果當(dāng)前存在事務(wù),則拋出異常走孽。
PROPAGATION_NESTED 如果當(dāng)前存在事務(wù)惧辈,則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù)磕瓷,則執(zhí)行與PROPAGATION_REQUIRED類似的操作盒齿。
事務(wù)的隔離級別
數(shù)據(jù)庫事務(wù)的隔離級別有4種念逞,由低到高分別為Read uncommitted 、Read committed 、Repeatable read 塘匣、Serializable 翎迁。而且,在事務(wù)的并發(fā)操作中可能會出現(xiàn)臟讀审洞,不可重復(fù)讀,幻讀待讳。
Read uncommitted:讀未提交芒澜,顧名思義,就是一個事務(wù)可以讀取另一個未提交事務(wù)的數(shù)據(jù)创淡。會出現(xiàn)臟讀痴晦、不可重復(fù)讀、幻讀 ( 隔離級別最低琳彩,并發(fā)性能高 )
Read committed:讀提交誊酌,顧名思義,就是一個事務(wù)要等另一個事務(wù)提交后才能讀取數(shù)據(jù)露乏。會出現(xiàn)不可重復(fù)讀碧浊、幻讀問題(鎖定正在讀取的行)
Repeatable read:重復(fù)讀,就是在開始讀取數(shù)據(jù)(事務(wù)開啟)時瘟仿,不再允許修改操作箱锐,會出幻讀(鎖定所讀取的所有行)。
SERIALIZABLE :串行化 可解決臟讀 幻讀 不可重復(fù)讀
Mysql的默認(rèn)隔離級別是Repeatable read劳较,但mysql不會引起幻讀問題(它使用了間隙鎖)
什么是臟讀幻讀不可重復(fù)讀驹止?
出現(xiàn)情況:當(dāng)我們多個事務(wù)同時執(zhí)行的去操作同一個數(shù)據(jù)的時候容易導(dǎo)致臟讀、不可重復(fù)讀观蜗、幻讀的情況產(chǎn)生臊恋。
臟讀 :臟讀就是指當(dāng)一個事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改墓捻,而這種修改還沒有提交到數(shù)據(jù)庫中抖仅,這時,另外一個事務(wù)也訪問 這個數(shù)據(jù)砖第,然后使用了這個數(shù)據(jù)撤卢。
不可重復(fù)讀 :在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,稱為是不 可重復(fù)讀厂画。例如凸丸,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間袱院,作者重寫了該文檔屎慢。當(dāng)編輯人員第二次讀取文檔時瞭稼,文檔已更改。原始讀取不可重復(fù)腻惠。如果 只有在作者全部完成編寫后編輯人員才可以讀取文檔环肘,則可以避免該問題。
幻讀 : 是指當(dāng)事務(wù)不是獨(dú)立執(zhí)行時發(fā)生的一種現(xiàn)象集灌,例如第一個事務(wù)對一個表中的數(shù)據(jù)進(jìn)行了修改悔雹,這種修改涉及到表中的全部數(shù)據(jù)行。 同時欣喧,第二個事務(wù)也修改這個表中的數(shù)據(jù)腌零,這種修改是向表中插入一行新數(shù)據(jù)。那么唆阿,以后就會發(fā)生操作第一個事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行益涧,就好象 發(fā)生了幻覺一樣。
什么是分布式事務(wù)驯鳖?
在我們單體項(xiàng)目中闲询,由于是使用的是同一個數(shù)據(jù)庫,是同一個數(shù)據(jù)源浅辙,而在分布式環(huán)境下我們會存在同一組事務(wù)需要去訪問不同數(shù)據(jù)源的情況扭弧,也就是跨庫的情況,要保證數(shù)據(jù)的一致性就需要用到我們的分布式事務(wù)记舆。
常見的分布式事務(wù)的解決方案鸽捻?
方案:2pc(兩段式提交) Tcc seata 可靠消息的最終一致性 最大努力通知
2pc:兩階段提交協(xié)議,就是將事務(wù)流程分為兩個階段氨淌,p( Prepare phase) 準(zhǔn)備階段 c ( commit phase ) 提交階段
實(shí)現(xiàn)方式:
在第一個階段 準(zhǔn)備階段泊愧,事務(wù)管理器tm會發(fā)送請求去詢問各個事務(wù)是否準(zhǔn)備好伊磺,如果都準(zhǔn)備好了盛正,進(jìn)入第二個階段 提交階段 提交A B服務(wù)的事務(wù)。如果在有任何一個服務(wù)沒準(zhǔn)備好屑埋,那么事務(wù)管理器會通知各個服務(wù)進(jìn)行事務(wù)回滾
這個方案會出現(xiàn)的問題:
1.第一階段豪筝,某一個服務(wù)很久沒有回復(fù)事務(wù)管理器它的準(zhǔn)備情況,會一直等待摘能,造成堵塞
2.第二個階段续崖,事務(wù)管理器在發(fā)出提交命令后就宕機(jī)了,或者是服務(wù)在接收到提交事務(wù)命令后也宕機(jī)了团搞,這種會導(dǎo)致處理結(jié)果不一致的情況严望。
seata
因?yàn)?pc(兩段式提交)的方式具有許多問題,我們一般都是采用的seata來處理分布式事務(wù)
seata里面有:
TC:事務(wù)協(xié)調(diào)器 管理分支事務(wù)和全局狀態(tài) 發(fā)布提交或回滾指令
TM:事務(wù)管理器 管理全局事務(wù)
RM:資源管理器 管理分支事務(wù)
實(shí)現(xiàn)原理:
操作開始的時候
1.TM會向TC發(fā)送請求注冊一個全局事務(wù)逻恐,并生成一個全局事務(wù)XID像吻,然后A服務(wù)向TC注冊一個分布式事務(wù)峻黍,并納入XID管理
2.A服務(wù)完成操作后,提交分支事務(wù)拨匆,并告知TC完成情況姆涩,同時會向seata的undolog日志中插入一條數(shù)據(jù)。然后攜帶著XID去調(diào)用B服務(wù)惭每。
3.B服務(wù)也會去TC注冊一個分布式事務(wù)骨饿,并納入XID管理。
4.B服務(wù)完成服務(wù)后台腥,提交分支事務(wù)宏赘,并告知TC完成情況,同時會向seata的undolog日志中插入一條數(shù)據(jù)
5.TC如果收到的所有分支事務(wù)的回復(fù)都是成功的黎侈,那么會發(fā)布提交全局事務(wù)的指令置鼻,并刪除seata undolog中的數(shù)據(jù)
6.TC如果收到的分支事務(wù)中有失敗的回復(fù),那么會發(fā)布回滾指令蜓竹,根據(jù)undolog日志的XID去進(jìn)行回滾操作箕母。
TCC
try 嘗試 confirm 確認(rèn) cancle取消 (跟2pc有點(diǎn)相似,也是兩個階段)
實(shí)現(xiàn)原理:
try :鎖定資源俱济,會有一個軟狀態(tài)
鎖定資源后會告知事務(wù)管理器我已經(jīng)更改狀態(tài)了嘶是,然后根據(jù)各個分支事務(wù)的狀態(tài)來確定時進(jìn)行confirm 還是 cancle;
confirm:如果事務(wù)全部成功,更改軟狀態(tài)
cancle:如果事務(wù)有失敗的蛛碌,調(diào)用各個服務(wù)的cancel釋放資源
存在問題:
與代碼的耦合度較高聂喇,同時還要保證各個接口的冪等性問題,導(dǎo)致項(xiàng)目維護(hù)困難蔚携。
可靠消息最終一致性:
借助消息中間鍵希太,進(jìn)行解耦和異步,但是消息中間鍵需要支持事務(wù)酝蜒,目前只有rocketmq支持事務(wù)消息誊辉。
考慮問題:分支事務(wù)和發(fā)送消息的原子性?
解決:通過發(fā)送消息的一個確認(rèn)回調(diào)來解決
消息接收的可靠性亡脑?
解決:ack機(jī)制
重復(fù)消費(fèi)問題 堕澄?
解決:接口冪等性
1.A服務(wù)發(fā)送一個half消息到mq,這個half消息是發(fā)送到一個特殊的日志中霉咨,對于其他消費(fèi)者而言是不可見的蛙紫。
2.Mq接收到消息,回調(diào)一個消息告知A服務(wù)途戒,消息發(fā)送成功坑傅,A服務(wù)可以提交本地事務(wù)
3.A提交本地事務(wù),并告知mq本地事務(wù)提交的一個狀態(tài)(回滾/提交)
4.Mq接收到A發(fā)送過來的本地事務(wù)的狀態(tài),如果是提交喷斋,此時將half消息放入B的消費(fèi)隊(duì)列中唁毒,如果是回滾矢渊,刪除half消息。
5.如果A服務(wù)因?yàn)榫W(wǎng)絡(luò)問題遲遲沒有給mq發(fā)送本地事務(wù)狀態(tài)枉证,此時mq主動調(diào)用A服務(wù)提供的事務(wù)查詢接口 矮男,獲取本地事務(wù)的狀態(tài),決定half的留存
最大努力通知:
主要用于接入外部平臺的情況
外部平臺會主動去通知調(diào)用服務(wù)室谚,如果多次通知后毡鉴,都沒有成功,就不會再去通知調(diào)用服務(wù)秒赤,但是外部平臺會提供一個接口猪瞬,當(dāng)調(diào)用服務(wù)恢復(fù)后,可以主動去調(diào)用這個接口查詢入篮。