初識事務,事務隔離級別,事務傳播行爲

本篇文章會介紹如下幾個概念:事務,事務隔離級別,spring事務的傳播模式。在介紹事務時會引出原子性的概念,在介紹事務隔離級別的時候會引出髒讀和幻讀的概念。html

事務

什麼是事務?

事務最開始是數據庫中的概念,它把一系列的操做統一爲一個總體,這一系列的操做要麼同時成功,要麼同時失敗。一個事務基本的操做是:java

  1. 開啓事務
  2. 若是發生了錯誤,進行回滾
  3. 若是沒有發生錯誤,則提交事務

爲何要有事務?

在咱們處理簡單業務的時候,好比說一條插入數據的操做,只會獲得兩個結果,要麼插入成功,要麼插入失敗,這對應到代碼邏輯上是很簡單的。
咱們稱這樣的操做具備原子性。spring

可是咱們的業務每每不會只有插入一條數據那麼簡單,可能用戶點擊一個按鈕後,咱們須要插入一條數據和刪除一條數據。因爲每一個操做都有可能成功和失敗,這個時候咱們就有了2^2=4種狀況,這下編程起來就麻煩了。sql

爲了方便編程(也爲了符合實際業務邏輯),咱們引入開頭所述的事務機制,把兩個操做放在一個事務裏面,使這兩個操做具有原子性,這樣一來業務處理起來就方便多了。數據庫

事務隔離級別

上面咱們談到了操做數據庫的時候會使用到事務,接下來引入的問題就是:在數據庫中,不免會出現多個事務同時操做數據的狀況,這時數據庫設置的事務隔離級別不一樣,會出現不一樣的數據操做結果,經典的髒讀與幻讀也誕生於此。編程

首先給出併發訪問時,不一樣事務隔離級別下的狀況表:ubuntu

事務隔離級別 髒讀 不可重複讀 幻讀
read uncommitted 讀未提交
read committed 讀提交 不會
repeatable read 可重複讀 不會 不會
serializable 串行化 不會 不會 不會

能夠看到事務隔離級別越高,產生的問題越少,可是相應的性能是會下降的,下面經過幾個例子分別闡述不一樣事務隔離級別下發生的問題:併發

讀未提交ide

當事務隔離設置爲讀未提交時,最容易產生的問題是髒讀。讀未提交指的是當前事務能夠讀到其餘事務未提交的數據:假設一位父親給他的兒子打生活費,原本一個月2000可是不當心手抖多打了1000變成3000,這時兒子去商店消費,查詢餘額的時候就多了3000。因爲父親的事務還沒提交,便立馬回滾事務,從新打過去2000生活費。性能

這個時候兒子讀到的餘額就是髒數據,產生的緣由是讀取了其餘事務未提交的數據。

讀提交

只要將事務隔離級別設置爲讀提交就能解決上面的髒讀問題,他能保證當前事務只能讀到其餘事務已經提交的數據。可是讀提交會面臨一個新問題:不可重複讀。

好比說兒子拿着卡到商店消費,買單的時候(開啓當前事務),系統檢測到卡里只有500元。這個時候父親給兒子轉了2000塊生活費,當兒子準備扣費的時候再查詢餘額發現變成了2500元(這個查詢發生在父親的轉帳事務提交以後)

在同一個事務中,兒子的餘額在不一樣的時候讀取的值不同,這就是不可重複讀問題。想要解決這個問題,須要把事務隔離級別設置爲可重複讀

可重複讀

在可重複讀的狀況下,當前事務會禁止其餘事務對正在操做的數據進行更新,這樣一來,父親轉帳的事務就要等到兒子帳號扣費結束後才能進行,今後解決了不可重複讀問題。

可是可重複讀級別下還可能發生一個問題叫幻讀,舉例以下:兒子今天在外消費了1000元,父親查看兒子一天的消費記錄(開啓事務),發現一共是1000元。這個時候兒子又消費了1000元(父親的事務仍在進行中),接着父親打印兒子今天的消費記錄,發現莫名其妙地變成了2000元,多了一條消費記錄。

像這種當前事務在操做的過程當中,因爲別的事務增長或刪除數據,致使當前事務操做的數據忽然變多或變少的狀況,就叫幻讀。想要解決幻讀,須要把事務隔離級別升級爲串行化

串行化

當事務隔離級別爲串行化時,全部事務都是串行執行的,對應上面的例子:父親在查看當天消費記錄時,兒子是不能消費的。這麼一來事務併發帶來的問題都能解決,可是效率很低。

擴展

在經常使用的數據庫中Orcale默認的事務隔離級別是讀提交,而Mysql默認的是可重複讀。在Mysql的InnoDB引擎中,雖然事務隔離級別是可重複讀,可是也能夠解決幻讀問題,背後的原理是在數據行之間添加間隙鎖,防止數據的插入與刪除。

具體選擇那一種事務隔離級別,要看具體的業務須要

事務傳播行爲

事務的傳播行爲從字面上也是挺好理解的:想要發生傳播就必定要有兩個以上的物體,而這裏指的是兩個方法都要在事務中進行,當一個事務方法A調用另外一個事務方法B時,另外一個事務方法B該如何運行。

Spring一共定義了7種事務傳播行爲(事務方法B該如何運行):

傳播行爲 含義
PROPAGATION_REQUIRED 若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中(這是最多見的選擇,也是spring的默認事務傳播行爲)
PROPAGATION_SUPPORTS 支持當前事務,若是當前沒有事務,就以非事務方式執行
PROPAGATION_MANDATORY 使用當前的事務,若是當前沒有事務,就拋出異常
PROPAGATION_REQUIRES_NEW 新建事務,若是當前存在事務,把當前事務掛起
PROPAGATION_NOT_SUPPORTED 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起
PROPAGATION_NEVER 以非事務方式執行,若是當前存在事務,則拋出異常
PROPAGATION_NESTED 若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做

平時咱們最經常使用的是 PROPAGATION_REQUIRED,這也是spring的默認事務傳播行爲,理解了它就能按理推導其餘的事務傳播行爲。

好比說當前咱們有以下代碼:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
     methodB();
    // do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}
  1. 假設咱們執行methodA,因爲當前尚未事務,因而就新建立一個事務。
  2. 當在methodA中調用methodB的時候,因爲methodA已經存在於事務中,因而methodB便無需新建立一個事務,直接加入到methodA的事務中便可。

總結

  1. 事務可以讓一系列不一樣的操做具備原子性。(固然事務具有ACID四大特性,本文在初步介紹時強調的是原子性)
  2. 事務隔離級別定義了事務併發操做時的訪問規則。
  3. 事務傳播行爲定義了事務方法在執行時該怎麼運用事務。

參考文章: 事務隔離級別:https://www.cnblogs.com/ubuntu1/p/8999403.html 事務傳播行爲:https://blog.csdn.net/weixin_39625809/article/details/80707695 事務傳播行爲:https://www.cnblogs.com/softidea/p/5962612.html

相關文章
相關標籤/搜索