什么是事務(wù)戏罢?
發(fā)生錯誤屋谭,回滾操作。
要么全部正確執(zhí)行帖汞,要么全不執(zhí)行。
事務(wù)四大特性
ACID 原子性Atomicity凑术,一致性Consistency翩蘸,隔離性Isolation,持久性Durability
- 原子性:事務(wù)里是一個不可分割整體淮逊,要么一起執(zhí)行催首,要么一起不執(zhí)行。
- 一致性:事務(wù)執(zhí)行前后泄鹏,數(shù)據(jù)庫一致性沒有被破壞郎任。提交事務(wù)沒有中間狀態(tài)。要么提交前备籽,要么提交后舶治。
- 隔離性:事務(wù)之間互不干擾。
- 持久性:提交了就永久改變了车猬,不會不生效霉猛。
事務(wù)隔離級別
讀未提交 read-uncommitted
- A還沒提交,B就讀了珠闰。若A rollback惜浅,B讀到錯誤數(shù)據(jù)(臟讀)。
不可重復(fù)讀/讀提交 read-committed
- 提交了才能讀
- A讀伏嗜,B修改并提交坛悉,A再讀變了(不可重復(fù)讀)
可重復(fù)讀 repeatable-read
- 讀時不可修改(行鎖)A讀伐厌,B不準(zhǔn)改,A再讀完成裸影,B改
- A讀(范圍)挣轨,B插入并提交(不修改A鎖的),A再讀空民,個數(shù)變了(幻讀)
串行化/可 serializable
- 讀時不可增刪改(表鎖)
事務(wù)隔離級別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
---|---|---|---|
讀未提交(read-uncommitted) | ? | ? | ? |
不可重復(fù)讀(read-committed) | ? | ? | ? |
可重復(fù)讀(repeatable-read) | ? | ? | ? |
串行化(serializable) | ? | ? | ? |
級別越高刃唐,執(zhí)行效率就越低。
- MySQL默認(rèn)
可重復(fù)讀
界轩。 - Oracle默認(rèn)
讀提交
画饥,支持read committed 和 serializable。另外還支持read only(只讀浊猾,不可修改) 和 read write(默認(rèn))
事務(wù)傳播級別
7種:
傳播級別 | 描述 | 上下文存在事務(wù) | 上下文不存在事務(wù) |
---|---|---|---|
REQUIRED | 默認(rèn) | 加入事務(wù)中 | 新建事務(wù) |
SUPPORTS | 支持 | 加入事務(wù)中 | 非事務(wù)方式執(zhí)行 |
MANDATORY | 強制上下文有事務(wù) | 加入事務(wù) | 拋出異常 |
REQUIRES_NEW | 新事務(wù) | 新建事務(wù)抖甘,掛起上下文事務(wù) | 新建事務(wù) |
NOT_SUPPORTED | 不支持 | 掛起上下文事務(wù),執(zhí)行當(dāng)前邏輯 | 新建事務(wù) |
NEVER | 不可有上下文事務(wù) | 拋出runtime異常,強制停止 | 非事務(wù)方式執(zhí)行 |
NESTED | 嵌套執(zhí)行 | 子回滾,父不影響缰冤;父回滾俏扩,子回滾;子先提交陶因,父后提交 | 新建事務(wù) |
一致性解決方案
兩階段提交(2PC),三階段提交(3PC),補償事務(wù)(TCC)柄沮,消息中間件(MQ異步確保)& 最大努力通知(定期校對)
兩階段提交(2PC)
Prepare、Commit 兩個階段
準(zhǔn)備階段
1)協(xié)調(diào)者節(jié)點向所有參與者節(jié)點詢問是否可以執(zhí)行提交操作(vote)废岂,并開始等待各參與者節(jié)點的響應(yīng)祖搓。
2)參與者節(jié)點執(zhí)行詢問發(fā)起為止的所有事務(wù)操作,并將Undo信息和Redo信息寫入日志湖苞。(注意:若成功這里其實每個參與者已經(jīng)執(zhí)行了事務(wù)操作)
3)各參與者節(jié)點響應(yīng)協(xié)調(diào)者節(jié)點發(fā)起的詢問拯欧。如果參與者節(jié)點的事務(wù)操作實際執(zhí)行成功,則它返
回一個”同意”消息财骨;如果參與者節(jié)點的事務(wù)操作實際執(zhí)行失敗镐作,則它返回一個”中止”消息。
提交階段
當(dāng)協(xié)調(diào)者節(jié)點從所有參與者節(jié)點獲得的相應(yīng)消息都為”同意”時:
1)協(xié)調(diào)者節(jié)點向所有參與者節(jié)點發(fā)出”正式提交(commit)”的請求隆箩。
2)參與者節(jié)點正式完成操作滑肉,并釋放在整個事務(wù)期間內(nèi)占用的資源。
3)參與者節(jié)點向協(xié)調(diào)者節(jié)點發(fā)送”完成”消息摘仅。
4)協(xié)調(diào)者節(jié)點受到所有參與者節(jié)點反饋的”完成”消息后靶庙,完成事務(wù)。如果任一參與者節(jié)點在第一階段返回的響應(yīng)消息為”中止”,或者 協(xié)調(diào)者節(jié)點在第一階段的詢問超時之前無法獲取所有參與者節(jié)點的響應(yīng)消息時:
1)協(xié)調(diào)者節(jié)點向所有參與者節(jié)點發(fā)出”回滾操作(rollback)”的請求六荒。
2)參與者節(jié)點利用之前寫入的Undo信息執(zhí)行回滾护姆,并釋放在整個事務(wù)期間內(nèi)占用的資源。
3)參與者節(jié)點向協(xié)調(diào)者節(jié)點發(fā)送”回滾完成”消息掏击。
4)協(xié)調(diào)者節(jié)點受到所有參與者節(jié)點反饋的”回滾完成”消息后卵皂,取消事務(wù)。
2PC缺點:
- 同步阻塞問題砚亭。執(zhí)行過程中灯变,所有參與節(jié)點都是事務(wù)阻塞型的。當(dāng)參與者占有公共資源時捅膘,其他第三方節(jié)點訪問公共資源不得不處于阻塞狀態(tài)添祸。
- 單點故障。由于協(xié)調(diào)者的重要性寻仗,一旦協(xié)調(diào)者發(fā)生故障刃泌。參與者會一直阻塞下去。尤其在第二階段署尤,協(xié)調(diào)者發(fā)生故障耙替,那么所有的參與者還都處于鎖定事務(wù)資源的狀態(tài)中,而無法繼續(xù)完成事務(wù)操作曹体。(如果是協(xié)調(diào)者掛掉俗扇,可以重新選舉一個協(xié)調(diào)者,但是無法解決因為協(xié)調(diào)者宕機導(dǎo)致的參與者處于阻塞狀態(tài)的問題)
- 數(shù)據(jù)不一致箕别。在二階段提交的階段二中铜幽,當(dāng)協(xié)調(diào)者向參與者發(fā)送commit請求之后,發(fā)生了局部網(wǎng)絡(luò)異尘吭校或者在發(fā)送commit請求過程中協(xié)調(diào)者發(fā)生了故障啥酱,這回導(dǎo)致只有一部分參與者接受到了commit請求爹凹。而在這部分參與者接到commit請求之后就會執(zhí)行commit操作厨诸。但是其他部分未接到commit請求的機器則無法執(zhí)行事務(wù)提交。于是整個分布式系統(tǒng)便出現(xiàn)了數(shù)據(jù)部一致性的現(xiàn)象禾酱。
- 二階段無法解決的問題:協(xié)調(diào)者再發(fā)出commit消息之后宕機微酬,而唯一接收到這條消息的參與者同時也宕機了。那么即使協(xié)調(diào)者通過選舉協(xié)議產(chǎn)生了新的協(xié)調(diào)者颤陶,這條事務(wù)的狀態(tài)也是不確定的颗管,沒人知道事務(wù)是否被已經(jīng)提交。
三階段提交(3PC)
CanCommit滓走、PreCommit垦江、DoCommit三個階段
- 引入超時機制。同時在協(xié)調(diào)者和參與者中都引入超時機制搅方。
- 在第一階段和第二階段中插入一個準(zhǔn)備階段比吭。保證了在最后提交階段之前各參與節(jié)點的狀態(tài)是一致的绽族。
CanCommit階段
協(xié)調(diào)者向參與者發(fā)送commit請求,參與者如果可以提交就返回Yes響應(yīng)衩藤,否則返回No響應(yīng)吧慢。
事務(wù)詢問 協(xié)調(diào)者向參與者發(fā)送CanCommit請求。詢問是否可以執(zhí)行事務(wù)提交操作赏表。然后開始等待參與者的響應(yīng)检诗。
響應(yīng)反饋 參與者接到CanCommit請求之后,正常情況下瓢剿,如果其自身認(rèn)為可以順利執(zhí)行事務(wù)逢慌,則返回Yes響應(yīng),并進(jìn)入預(yù)備狀態(tài)跋选。否則反饋No
PreCommit階段
- 假如協(xié)調(diào)者從所有的參與者獲得的反饋都是Yes響應(yīng)涕癣,那么就會執(zhí)行事務(wù)的預(yù)執(zhí)行。
- 發(fā)送預(yù)提交請求 協(xié)調(diào)者向參與者發(fā)送PreCommit請求前标,并進(jìn)入Prepared階段坠韩。
- 事務(wù)預(yù)提交 參與者接收到PreCommit請求后,會執(zhí)行事務(wù)操作炼列,并將undo和redo信息記錄到事務(wù)日志中只搁。
- 響應(yīng)反饋 如果參與者成功的執(zhí)行了事務(wù)操作,則返回ACK響應(yīng)俭尖,同時開始等待最終指令氢惋。
- 假如有任何一個參與者向協(xié)調(diào)者發(fā)送了No響應(yīng),或者等待超時之后稽犁,協(xié)調(diào)者都沒有接到參與者的響應(yīng)焰望,那么就執(zhí)行事務(wù)的中斷。
- 發(fā)送中斷請求 協(xié)調(diào)者向所有參與者發(fā)送abort請求已亥。
- 中斷事務(wù) 參與者收到來自協(xié)調(diào)者的abort請求之后(或超時之后熊赖,仍未收到協(xié)調(diào)者的請求),執(zhí)行事務(wù)的中斷虑椎。
相對于2PC震鹉,3PC主要解決的單點故障問題,并減少阻塞捆姜,因為一旦參與者無法及時收到來自協(xié)調(diào)者的信息之后传趾,他會默認(rèn)執(zhí)行commit。而不會一直持有事務(wù)資源并處于阻塞狀態(tài)泥技。但是這種機制也會導(dǎo)致數(shù)據(jù)一致性問題浆兰,因為,由于網(wǎng)絡(luò)原因,協(xié)調(diào)者發(fā)送的abort響應(yīng)沒有及時被參與者接收到簸呈,那么參與者在等待超時之后執(zhí)行了commit操作宽涌。這樣就和其他接到abort命令并執(zhí)行回滾的參與者之間存在數(shù)據(jù)不一致的情況。
補償事務(wù)(TCC)
基于業(yè)務(wù)層面的事務(wù)定義蝶棋。
鎖粒度完全由業(yè)務(wù)自己控制卸亮。
本質(zhì)是一種補償?shù)乃悸贰?br> 把事務(wù)運行過程分成 Try、Confirm / Cancel 兩個階段
Try :嘗試執(zhí)行業(yè)務(wù)
- 完成所有業(yè)務(wù)檢查( 一致性 )
- 預(yù)留必須業(yè)務(wù)資源( 準(zhǔn)隔離性 )
Confirm / Cancel 階段:
- Confirm :
真正執(zhí)行業(yè)務(wù)
不做任務(wù)業(yè)務(wù)檢查
Confirm 操作滿足冪等性 - Cancel :
釋放 Try 階段預(yù)留的業(yè)務(wù)資源
Cancel 操作滿足冪等性
Confirm 與 Cancel 互斥
本地消息表
將需要分布式處理的任務(wù)通過消息日志的方式來異步執(zhí)行玩裙。消息日志可以存儲到本地文本兼贸、數(shù)據(jù)庫或消息隊列,再通過業(yè)務(wù)規(guī)則自動或人工發(fā)起重試吃溅。
本地消息表記錄狀態(tài)溶诞,定時掃描,重新發(fā)送失敗的消息
消息中間件(MQ異步確保) 最大努力通知(定期校對)
RocketMQ
業(yè)務(wù)方法內(nèi)要向消息隊列提交兩次請求决侈,一次發(fā)送消息和一次確認(rèn)消息螺垢。如果確認(rèn)消息發(fā)送失敗了RocketMQ會定期掃描消息集群中的事務(wù)消息,這時候發(fā)現(xiàn)了Prepared消息赖歌,它會向消息發(fā)送者確認(rèn)枉圃,所以生產(chǎn)方需要實現(xiàn)一個check接口,RocketMQ會根據(jù)發(fā)送端設(shè)置的策略來決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息庐冯。這樣就保證了消息發(fā)送與本地事務(wù)同時成功或同時失敗孽亲。
系統(tǒng)A除了實現(xiàn)正常的業(yè)務(wù)流程外,還需提供一個事務(wù)詢問的接口展父,供消息中間件調(diào)用返劲。當(dāng)消息中間件收到一條事務(wù)型消息后便開始計時,如果到了超時時間也沒收到系統(tǒng)A發(fā)來的Commit或Rollback指令的話栖茉,就會主動調(diào)用系統(tǒng)A提供的事務(wù)詢問接口詢問該系統(tǒng)目前的狀態(tài)篮绿。該接口會返回三種結(jié)果:
提交:將該消息投遞給系統(tǒng)B。
回滾:直接將條消息丟棄吕漂。
處理中:繼續(xù)等待亲配。