mysql innodb中的四種事務(wù)隔離級別

四種隔離級別

隔離級別 臟讀(Dirty Read) 臟讀(Dirty Read) 幻讀(Phantom Read)
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重復(fù)讀(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
  1. 未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù)
  2. 提交讀(Read Committed):只能讀取到已經(jīng)提交的數(shù)據(jù)。Oracle等多數(shù)數(shù)據(jù)庫默認都是該級別 (不重復(fù)讀)
  3. 可重復(fù)讀(Repeated Read):可重復(fù)讀。在同一個事務(wù)內(nèi)的查詢都是事務(wù)開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重復(fù)讀,但是還存在幻象讀
  4. 串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

詳細說明

以下表(test)解釋各個隔離級別,只有兩個字段,一個id,一個account

image.png

插入測試數(shù)據(jù)


image.png

關(guān)閉mysql自動提交和設(shè)置隔離級別

  • 查看是否開啟自動提交
show variables like 'autocommit';
  • 打開或關(guān)閉自動提交
set autocommit = 1;//打開
set autocommit = 0;//關(guān)閉
  • 查看數(shù)據(jù)庫隔離級別
select @@tx_isolation;//當前會話隔離級別
select @@global.tx_isolation;//系統(tǒng)隔離級別
  • 設(shè)置數(shù)據(jù)庫隔離級別(當前會話)
SET session transaction isolation level read uncommitted;
SET session transaction isolation level read committed;
SET session transaction isolation level REPEATABLE READ;
SET session transaction isolation level Serializable;

未提交讀(Read uncommitted)

關(guān)閉自動提交、設(shè)置對應(yīng)隔離級別,開啟兩個會話,下面不在贅述

image.png
  1. 會話A


    image.png
  2. 會話B


    image.png
  3. 會話A中插入一條記錄,看B中情況
  • 會話A


  • 會話B


    image.png
  • 結(jié)論
    我們發(fā)現(xiàn)會話A中事務(wù)并沒有提交但是在會話B中卻可以看到會話A中插入的記錄,這種情況就是臟讀。

已提交讀(Read committed)

設(shè)置會話A、B隔離級別為已提交讀, 目前會話A和會話B中都只有4條記錄,如下:

image.png
  1. 會話A


    image.png
  2. 會話B


    image.png
  3. 會話A commit以后,會話B的情況


    image.png

    4. 結(jié)論
    當會話A中的事務(wù)沒有提交的時候,會話B中是看不到A中插入的記錄,不存在臟讀的情況。但是當隔離級別為提交讀(Read Committed)時候,會存在不可重復(fù)讀的情況,實驗如下:

  4. 會話A和B開啟事務(wù),當A中插入一條記錄并提交的情況中,會話B的事務(wù)中存在前后兩次讀取不一致的情況。
  • 會話A


  • 會話B在A插入id=5這條記錄的前后情況如下:
A中沒有commit之前
A中commit之后

可重復(fù)讀(Repeatable read)

會話A、B設(shè)置隔離級別為可重復(fù)讀(Repeatable read)

  1. 會話A


    會話A插入記錄并提交前后情況
  2. 會話B


    會話A插入記錄提交之前

    會話A插入記錄提交之后
  3. 結(jié)論
    我們發(fā)現(xiàn)無論是在會話A插入記錄并提交之前還是提交之后,會話B中都看不到剛剛A插入的id=7的那條記錄,既不存在在隔離級別為Repeatable read中的不可重復(fù)讀的情況。無論A中插入、更新、刪除,B中都是不可見的,即在Repeatable read級別下,B是可重復(fù)讀的。我們都知道還有一個幻讀的問題,為什么都可重復(fù)讀了,還存在幻讀的問題?mysql又是如何解決幻讀的問題的呢?

幻讀的定義

官網(wǎng)的定義:

The so-called phantom problem occurs within a transaction when the 
same query produces different sets of rows at different times. For 
example, if a [`SELECT`]
(https://dev.mysql.com/doc/refman/5.7/en/select.html "13.2.9 SELECT 
Syntax") is executed twice, but returns a row the second time that was
 not returned the first time, the row is a “phantom” row.

意思就是幻讀指在同一個事務(wù)中,兩次相同的查詢結(jié)果集不同。那這個又和不可重復(fù)讀有什么區(qū)別呢?確實這兩者有些相似。但不可重復(fù)讀重點在于update和delete,而幻讀的重點在于insert。

幻讀問題

設(shè)置會話A和會話B的隔壁級別為可重復(fù)讀(Repeatable read)

當前會話A和會話B的查詢情況如下
會話A:

session A

會話B:
session B

下面我們復(fù)現(xiàn)一下幻讀問題
會話A:
會話A

會話B(插入一條記錄):
image.png

我們再來看看會話A中情況,我們看看加鎖讀和不加鎖讀的區(qū)別:
會話A:
不加鎖讀

加鎖讀

我們發(fā)現(xiàn)在不加鎖時候,是可以重復(fù)讀的,加鎖時候讀到了額外的一條記錄,這個我們就稱之為幻讀。那么mysql如何解決幻讀的問題呢?答案是gap鎖,確切的說是next-key lock。nexy-key lock = record lock + gap lock。比如上面的例子,我們在會話A中執(zhí)行這條語句的時候(select * from test where account=300;)時候加鎖lock,如下:(select * from test where account=300 for update;)。那么會話B在插入(4,300)時候會被阻塞,因為有g(shù)ap鎖。這里因為我們沒有在account上加上索引,所以整個表都會被鎖(準確的說是accout整個范圍都會被鎖)。那么mysql何時獲取next-key lock?

何時獲取next-key lock

官網(wǎng)描述如下:

+ For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE 
MODE), UPDATE, and DELETE statements, locking depends on 
whether the statement uses a unique index with a unique search 
condition, or a range-type search condition.
+ For a unique index with a unique search condition, InnoDB locks only
 the index record found, not the gap before it.
+ For other search conditions, InnoDB locks the index range scanned, 
using gap locks or next-key locks to block insertions by other sessions 
into the gaps covered by the range. For information about gap locks and
 next-key locks, see Section 15.5.1, “InnoDB Locking”.

也就是locking reads,UPDATE和DELETE時,除了對唯一索引的條件外都會獲取gap鎖或next-key鎖。 當查詢的索引含有唯一屬性的時候,Next-Key Lock 會進行優(yōu)化,將其降級為Record Lock,即僅鎖住索引本身,不是范圍

可串行化(Serializable )

這個級別很簡單,讀加共享鎖,寫加排他鎖,讀寫互斥。使用的悲觀鎖的理論,實現(xiàn)簡單,數(shù)據(jù)更加安全,但是并發(fā)能力非常差。如果你的業(yè)務(wù)并發(fā)的特別少或者沒有并發(fā),同時又要求數(shù)據(jù)及時可靠的話,可以使用這種模式。select在這個級別在Serializable這個級別,還是會加鎖的!

mysql 的隔離級別最難理解的地方在可重復(fù)讀和幻讀的區(qū)別,我雖然想盡力去把這里說明白,但是寫的時候發(fā)現(xiàn)還是很難去描述清楚,這里我也看了很對的blog,也沒有發(fā)現(xiàn)能把mysql的鎖和隔離級別各個方面都講的很明白的地方,所以要想搞明白這個問題,還是得都看一些資料,集眾家之長,下面是我看的比較好的幾篇blog

Innodb中的事務(wù)隔離級別和鎖的關(guān)系-來自美團的技術(shù)團隊
mysql REPEATABLE READ對幻讀的解決
官網(wǎng)-幻讀
官網(wǎng)-事務(wù)隔離級別
官網(wǎng)-innodb鎖
我想你把上面的幾篇文章都看完了,應(yīng)該就能理解了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容