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

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

關(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)隔離級別,開啟兩個會話,下面不在贅述

-
會話A
image.png -
會話B
image.png - 會話A中插入一條記錄,看B中情況
-
會話A
-
會話B
image.png - 結(jié)論
我們發(fā)現(xiàn)會話A中事務(wù)并沒有提交但是在會話B中卻可以看到會話A中插入的記錄,這種情況就是臟讀。
已提交讀(Read committed)
設(shè)置會話A、B隔離級別為已提交讀, 目前會話A和會話B中都只有4條記錄,如下:

-
會話A
image.png -
會話B
image.png -
會話A commit以后,會話B的情況
image.png
4. 結(jié)論
當會話A中的事務(wù)沒有提交的時候,會話B中是看不到A中插入的記錄,不存在臟讀的情況。但是當隔離級別為提交讀(Read Committed)時候,會存在不可重復(fù)讀的情況,實驗如下: - 會話A和B開啟事務(wù),當A中插入一條記錄并提交的情況中,會話B的事務(wù)中存在前后兩次讀取不一致的情況。
-
會話A
- 會話B在A插入id=5這條記錄的前后情況如下:


可重復(fù)讀(Repeatable read)
會話A、B設(shè)置隔離級別為可重復(fù)讀(Repeatable read)
-
會話A
會話A插入記錄并提交前后情況 -
會話B
會話A插入記錄提交之前
會話A插入記錄提交之后 - 結(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:

會話B:

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

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

我們再來看看會話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)該就能理解了










