Mysql的隔離級別

weixin_33806914發表於2018-08-12

Mysql的隔離級別

事務的基本要素

  1. 原子性(Atomicity):我們學過化學,我們知道,原子是構成物質的基本單位。放在Mysql的事務中,我們可以理解為,一個事務要麼全部執行,要不全部不執行,不存在說一個事務中部分語句執行成功,部分語句執行不成功,存在不成功的語句將會全部回滾,所有語句當作是沒有執行過一樣。
  2. 一致性(Consistency):表示說事務的開始之前和開始之後,資料庫的完整性約束是沒有被破壞的,比如A要向B轉錢,不存在說A已經扣了款,B還沒有收到款
  3. 永續性(Durability):事務完成之後,事務對資料庫所做的修改將全部儲存在資料庫中。
  4. 隔離性(Isolation):表示在同一個時間內,只能有一個事務請求同一個資料,不同的事務之間彼此沒有任何干擾,就比如說事務A正在向銀行取錢,在A取錢的過程結束前,事務B不能向這張卡轉錢。

事務的併發問題

  1. 髒讀:表示說一個事務A讀取了事務B更新的資料,但是B發生錯誤回滾了,那麼A讀到的資料就是髒資料
  2. 不可重複讀:事務A多次讀取同一資料,在此過程中,事務B對這一資料進行了更改,因此事務A多次讀取同一資料時,結果不一致
  3. 幻讀:表示當一個事務A多次讀取一張分數表的總數時,事務B在這時候新增了一條分數,導致事務A兩次讀取的總數不一致。

注:不可重複度和幻讀很相似,很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。

事務的隔離級別

事務隔離級別 髒讀 不可重複度 幻讀
讀未提交(read-uncommitted)
不可重複讀(read-committed)
可重複讀(repeatable-read)
序列化(serializable)

例子

mysql 預設的事務隔離級別是repeatable-read

select @@tx_isolation;

可以通過上述的命令檢視mysql當前的事務隔離級別

set session transaction isolation level read committed;

上述命令可以設定事務隔離級別

clipboard.png

讀未提交(read-uncommitted)
  • 開啟事務A,並查詢表資料

clipboard.png

  • 另外開啟事務B,並對ID為1的hahah進行修改。

clipboard.png

  • 可以看到事務B已經修改,但是事務還沒提交,這時候我們回去事務A看看。

clipboard.png

-(忽略背景顏色哈)這時候我們可以發現,事務A的hahah的值也被修改了,也就是說事務A可以查詢到事務B已經更新的資料,如果這時候事務B由於某種原因回滾了,所有的操作將會被銷燬,那麼事務A查詢到的資料就是髒資料。如果在這時候事務A執行了update peter_test1 set score=score-10 where id=1的話,其實hahah的score也是為83,而不是73,因為事務B回滾了,但是其他的應用並不知道。所以解決這種問題的辦法就是採用不可重複讀(讀已提交,read-committed)

  • 開啟不可重複讀

clipboard.png

事務A和事務B都設定好

  • 檢視事務A的資料

clipboard.png

  • 修改事務B但是未提交

clipboard.png

這時候我們回來看看事務A

clipboard.png

這時候的事務B未提交,事務A不能查詢到事務B已經更新的資料,這就解決了髒讀的問題

  • 提交事務B

clipboard.png

下面我們來看看可重複讀

  • 開啟事務,並設定當前事務模式為repeatable read

clipboard.png

  • 事務A中查詢資料

clipboard.png

  • 在事務B中修改資料,並提交

clipboard.png

  • 檢視事務A的資料

clipboard.png

由上圖可以知道,儘管在事務B中提交了事務,但是在事務A的兩次查詢中的資料還是一樣的。這就是可以重複讀的效果,但是如果你在事務A中執行了update peter_test1 set score=score-10 where id=1;的話,hahah的資料並不是變成了73,而是變成了63,因為事務B在提交事務之後資料已經變成了73。

clipboard.png

可重複讀的隔離級別系愛是用了MVCC機制,A事務讀取的是記錄的快照版本,而非最新版本,B事務的更新是建立了一個新的版本來更新,不同的事務的讀和寫是分離的。(我也不是很清楚其中的意思,這不是我要了解的重點,往後再來學習哈)

下面我們來看看序列化(serializable)

  • 我想要去計算整張表的score總數

clipboard.png

  • 在事務B開始之後,新增一條資料,其中score為100,並提交

clipboard.png

  • 在事務A中重新計算整張表的score總數

clipboard.png

在事務A重新計算總數時,因為把事務B最新提交新增的值也給算進去了,所以兩次的計算結果是不一樣的。這就產生了幻讀。“序列化”是解決幻讀的一種方法

  • 設定當前事務模式為serializable。

clipboard.png

  • 在事務A開始計算的時候,事務B新增資料

clipboard.png

由圖中我們可以看到,事務B並沒有執行生效

  • 我們提交事務A

clipboard.png

  • 檢視事務B

clipboard.png

所以可以看到事務B在事務A執行完成之後,也就執行完成了。這也避免了幻讀的情況

mysql中事務隔離級別為serializable時會鎖表,因此不會出現幻讀的情況,這種隔離級別的併發性極低,開發中很少會用到。

注意:隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也很大,對於大多數的應用程式,可以優先考慮把資料庫的隔離級別設為read-committed,它能夠避免讀取髒資料,而且具有較好的併發效能,儘管他會導致不可重複讀,幻讀這些併發問題,但是在可能出現這種問題的個別場所,可以又應用程式採用悲觀鎖或者樂觀鎖來控制

相關文章