Spring 事務原理詳解

Spring 事務的使用

Spring 事務有編程式事務和聲明式事務。

常用的聲明式事務使用 @Transaction 註解就可以開啟事務。編程式事務不常用暫且不表。

@Transaction 是有參數的,我們可以設置事務的隔離級別,如:

@Transactional(isolation = Isolation.REPEATABLE_READ)

想要知道參數的意義,我們就要知道 Spring 事務的原理。

Spring 事務的原理

Spring 事務其實就是數據庫事務。

事務有四種隔離級別,使用如下:

讀未提交

@Transactional(isolation = Isolation.READ_UNCOMMITTED)

讀已提交

@Transactional(isolation = Isolation.READ_COMMITTED)

可重複讀

@Transactional(isolation = Isolation.REPEATABLE_READ)

串行化

@Transactional(isolation = Isolation.SERIALIZABLE)

下面我們依次講解這四個隔離級別有什麼用。

Spring 事務原理詳解

我們先以 MySQL 數據庫為例子,MySQL 的 InnoDB 引擎支持事務,提供了 行鎖表鎖

先下定義,加鎖 是為了防止併發的時候出錯。 行鎖 的性能要優於 表鎖,不加鎖 的性能要優於 行鎖。

得到結論,保證在併發不出錯的情況下能 不加鎖 就 不加鎖,能加 行鎖 就不要加 表鎖。

ok,下面開始分析四個隔離級別都加的什麼鎖。

讀未提交。

這個級別不加鎖,那麼程序裡使用這個隔離級別會出現什麼問題呢?

如果都是 select 語句,什麼問題都不會有,就用這個級別。如果有 update 語句,那就可能有問題了。

我們先提出這麼一個問題:

假如一個事務 A 先對某行數據進行修改,然後又改回去了。如果有另一個事務 B ,剛好卡在 A 的兩個操作中間對這行數據進行了一個讀取,那麼讀到的就是髒數據。

讀已提交。

針對上一個隔離級別的問題怎麼改進呢?對將要修改的數據加行鎖(對 update 語句加鎖),注意,注意,注意,加鎖了,安全了但是性能下降了。事務 A 對這行數據要修改,先加個鎖,直到結束才釋放鎖,事務 B 就沒辦法卡在 A 的操作中間對數據進行操作了。

但仍然有問題:

假如一個事務 A 先對某行數據進行讀取,然後進行加一保存。如果有另一個事務 B ,剛好卡在 A 的兩個操作中間對這行數據進行了一個讀取並加一保存,那麼數據就混亂了。

這個問題也可以表述為:

假如一個事務 A 先對某行數據進行讀取,然後進行讀取。如果有另一個事務 B ,剛好卡在 A 的兩個操作中間對這行數據進行了一個修改,那麼 A 兩次讀取的數據就不一樣了。

第一個表述更好理解,第二個表述主要為了突出了 不可重複讀 這幾個字,迎合下一個級別 可重複讀。

可重複讀。

針對上一個隔離級別的問題怎麼改進呢?對將要讀取的數據也加行鎖(對 select 語句加鎖),注意,注意,注意,上一個只有 update 語句加鎖,這個連 select 語句也加鎖了,安全了但是性能下降了。事務 A 對這行數據要讀取,先加個鎖,直到結束才釋放鎖,事務 B 就沒辦法卡在 A 的讀取的中間對數據進行操作了。

這個隔離級別仍然有問題:

假如事務 B 有一個 insert 語句,卡在事務 A 的某些語句中間插了一條數據,那麼就可能造成數據混亂,比如,我們對整個表操作, x 字段的值加 50,y 字段的值減 50,事務 B 卡在中間插入的數據只執行了 y 字段減 50,數據就混亂了。

串行化。

針對上一個隔離級別的問題,串行化加的是表鎖,什麼問題都不會有,但是性能很差。

綜上所述,事務隔離級別的選擇其實是我們程序併發情況下會出哪種錯誤,就用相應的隔離級別,在併發不出錯的前提下,儘可能的不加鎖,或者加行鎖而不是表鎖,這樣才能提高性能。


分享到:


相關文章: