一、數據庫事務的概念
用一句話簡單的說明:數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作(對數據庫的相關增刪改查的操作),要么完全地執行,要么完全地不執行。
二、數據庫事務的四大特性
數據庫的四大特性ACID,原子性、一致性、隔離性、持久性。每個特性都有其特定的職責。
2.1、原子性:一個事務中的所有操作,要不 操作全部成功,要不全部失敗,不能存在中間態。
2.2、一致性:事務必須使得數據庫從一個一致性狀態轉變到另一個一致性狀態。比如銀行轉賬,A賬戶轉到B賬戶,不管轉幾次,A和B賬戶的總額不能變。
2.3、隔離性:是指多個用戶同時請求數據庫,開啟多個事務同時處理某個數據庫,隔離性保證了各個事務之間均不受干擾,每個事務都感覺不到其他事務的存在。
2.4、持久性:對數據庫的修改是持久性的,一旦修改,就算數據庫系統出現故障,這種修改也不會丟失,這點是數據庫數據存放到硬盤中,並有redo log 和 binlog 一起保證的。
三、事務的隔離級別
隔離級別一共有四種:讀未提交、讀已提交、可重復讀、串行化。這四種隔離級別分別可以解決的不同的問題,下面先描述一下不使用隔離級別可能會出現的幾種問題。
(1)丟失修改
A和B兩個事物同事修改同一個數據,A修改的提交在B提交之后,導致B好像沒有修改,丟失修改。
(2)臟讀
B事務修改了一個數據並未提交,A事物讀取了這個數據,然后B事務回滾了,最后A又讀取了一次,兩次讀取的數據不一致,稱為臟讀。
(3)不可重復讀
A事務讀取了一個數據后,B事務修改了這個數據,A事務又讀取了這個數據,兩次讀取的數據也不一致,稱為不可重復讀。
(4)幻讀
A事務更新了某個字段(范圍是整個數據表的)(以id=1為條件的),B事務又插入了一條新的記錄,導致A事務認為自己沒有完全更新過來,就像出現幻覺一樣。
針對這幾種錯誤分別設置不同的隔離級別來解決:
第一種丟失修改一般使用加鎖鎖來解決,因此串行化可以解決,並且串行化可以解決上面出現的所有問題。
第二種問題臟讀是因為讀取其他事物未提交的數據,因為設置讀已提交隔離級別可以解決這個問題。但不可解決不可重復讀和幻讀的問題。
第三種問題不可能重復讀,是因為B事物的修改影響了A事務的讀取數據,設置可重復讀隔離級別,使得B事務修改數據和A事務讀取數據互不影響,隔離開來,從而解決這個問題,同時解決 臟讀問題。
第四種幻讀問題,是因為A事務更新完數據后,B事務又插入了新的數據,設置串行化隔離級別可解決,並且這種隔離級別解決上面所有的問題。除了串行化,多版本並發控制(MVCC, Multiversion Concurrency Control)機制也可以解決該問題。
InnoDB的MVCC,是通過在每行記錄后面保存兩個隱藏的列來實現的,這兩個列,分別保存了這個行的創建時間(系統版本號,沒開啟新事物自動遞增)和刪除時間(當刪除這行數據時,使當 前系統版本號作為刪除時間)。這里存儲的並不是實際的時間值,而是系統版本號 (可以理解為事務 的ID),每開始一個新的事務,系統版本號就會自動遞增,事務開始時刻的系統版本號會作為 事務的ID.不同版本號的行記錄相當於記錄的一個快照,不同的事務在不同的快照上處理自己的操 作,互不影響。
SELECT時的規則要求
InnoDB會根據以下兩個條件檢查每行記錄:
a.InnoDB只會查找版本早於當前事務版本的數據行(也就是,行的系統版本號小於或等於事務的系統版本號),這樣可以確保事務讀取的行,要么是在事務開始前已經存在的,要么是事務自身插 入或者修改過的.
b.行的刪除版本要么未定義,要么大於當前事務版本號,這可以確保事務讀取到的行,在事務開始之前未被刪除.
只有a,b同時滿足的記錄,才能返回作為查詢結果.
引用:https://blog.csdn.net/whoamiyang/article/details/51901888。
四、事務的傳播屬性
spring通過配置事務的傳播屬性來嚴格控制事務行為。比如 在一個配置了事務的方法中調用了另一個方法,則另一個方法應該怎么運行,是新開啟一個事務,還是和調用方法是一個事務,還 是 不開啟事務?一共有七種傳播屬性。
- PROPAGATION_REQUIRED(需要) 表示當前方法必須運行在事務中。如果當前事務存在,方法將會在該事務中運行。否則,會啟動一個新的事務。
- PROPAGATION_SUPPORTS (支持)表示當前方法不需要事務上下文,但是如果存在當前事務的話,那么該方法會在這個事務中運行,如果不存在事務就不在事務中執行。
- PROPAGATION_MANDATORY (強制必須)表示該當前方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常。
- PROPAGATION_REQUIRED_NEW(要求新事物) 表示當前方法必須運行在它自己的事務中。一個新的事務將被啟動。如果存在當前事務,在該方法執行期間,當前事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager。
- PROPAGATION_NOT_SUPPORTED(不支持新事物) 表示該方法不應該運行在事務中。如果存在當前事務,在該方法運行期間,當前事務將被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager。
- PROPAGATION_NEVER (從不)表示當前方法不應該運行在事務上下文中。如果當前正有一個事務在運行,則會拋出異常。
- PROPAGATION_NESTED(嵌套)(spring) 表示如果當前方法已經存在一個事務,那么該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,那么其行為與PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行為的支持是有所差異的。
五、分布式事務
分布式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分布式系統的不同節點之上。當數據庫的數據越來越大,數據庫無法承受起壓力,就會開始分庫分表,分庫分表后不同的庫就會分布在不同的服務器上,明顯的就會出現數據一致性問題。或者同一個事務中要調用的不同系統的不同的庫,也要保證要不全都成功,要不全部回滾。
如何解決分布式事務數據一致性問題,下面分別總結幾種方案。
(1)兩階段提交
事務協調器首先會將prepare消息寫入日志中,然后向AB數據庫發出prepare消息,操作完成后先不提交,而是返回yes,如果事務協調器收到的返回都是yes,則全部執行提交。如果有一個不 是yes,則向所有的執行器發送abort(退出、回滾)操作。
這種兩階段提交的方式能夠保證數據一致性。但是犧牲了一定的可用性。
優點: 盡量保證了數據的強一致,適合對數據強一致要求很高的關鍵領域。(其實也不能100%保證強一致)
缺點: 實現復雜,犧牲了可用性,對性能影響較大,不適合高並發高性能場景,如果分布式系統跨接口調用,目前 .NET 界還沒有實現方案。
(2)三段式提交協議
在二階段提交的基礎上,在提交的時候,先發出precommit(預提交),當所有的執行器返回yes,在最終發出提交操作申請。否則向所有的執行器發送abort(退出)請求。
最大優點就是降低了參與者的阻塞范圍,並且能夠在出現單點故障后繼續達成一致。
缺點就是在去除阻塞的情況下引入了新的問題,那就是參與者接收到了PreCommit消息,然后網絡出現問題,參與者和協調者無法通信,這種情況下,參與者依然會執行事務的提交。
(3)補償事務(TCC)
簡單舉個例子說明,比如一個借錢產品,你發起借錢時,你的可借額度會被凍結,借錢成功,扣減額度並解凍。借錢失敗則解凍額度,如果可借額度發生變化,還需要回滾額度。
優點: 跟2PC比起來,實現以及流程相對簡單了一些,但數據的一致性比2PC也要差一些
缺點: 缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬於應用層的一種補償方式,所以需要程序員在實現的時候多寫很多補償的代碼,在一些場景中,一些業務流程可能用TCC 定義及處理。
(4)本地消息表
消息生產方,需要額外建一個消息表,並記錄消息發送狀態。消息表和業務數據要在一個事務里提交,也就是說他們要在一個數據庫里面。然后消息會經過MQ發送到消息的消費方。如果消息 發 送失敗,會進行重試發送。
消息消費方,需要處理這個消息,並完成自己的業務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那么就會重試執行。如果是業務上面的失敗,可以給生產方發送 一個業務補償消息,通知生產方進行回滾等操作。
優點: 一種非常經典的實現,避免了分布式事務,實現了最終一致性。在 .NET中 有現成的解決方案。
缺點: 消息表會耦合到業務系統中,如果沒有封裝好的解決方案,會有很多雜活需要處理。
(5)基於可靠消息服務的分布式事務
有一些第三方的MQ是支持事務消息的,比如RocketMQ
- 在系統A處理任務A前,首先向消息中間件發送一條消息
- 消息中間件收到后將該條消息持久化,但並不投遞。此時下游系統B仍然不知道該條消息的存在。
- 消息中間件持久化成功后,便向系統A返回一個確認應答;
- 系統A收到確認應答后,則可以開始處理任務A;
- 任務A處理完成后,向消息中間件發送Commit請求。該請求發送完成后,對系統A而言,該事務的處理過程就結束了,此時它可以處理別的任務了。
- 但commit消息可能會在傳輸途中丟失,從而消息中間件並不會向系統B投遞這條消息,從而系統就會出現不一致性。這個問題由消息中間件的事務回查機制完成,下文會介紹。
- 消息中間件收到Commit指令后,便向系統B投遞該消息,從而觸發任務B的執行;
- 當任務B執行完成后,系統B向消息中間件返回一個確認應答,告訴消息中間件該消息已經成功消費,此時,這個分布式事務完成。
原文:https://blog.csdn.net/u010425776/article/details/79516298
參考:https://blog.csdn.net/u012092620/article/details/80222007