你們在面試中必定碰到過html
說說事務的隔離級別吧?mysql
老實說,事務隔離級別這個問題,不管是校招仍是社招,面試官都愛問!然而目前網上不少文章,說句實在話啊,我看了後我都懷疑做者弄懂沒!由於他們對可重複讀(Repeatable Read)和串行化(serializable)的解析實在是看的我一頭霧水! 再加上不少書都說可重複讀解決了幻讀問題,好比《mysql技術內幕--innodb存儲引擎》等,不一一列舉了,所以網上關於事務隔離級別的文章大可能是有問題的,因此再開一文說明! 本文所講大部份內容,皆有官網做爲佐證,所以對本文內容你能夠看完後,你徹底能夠當概念記在腦海裏,除非官網的開發手冊是錯的,不然應當無誤! 另外,本文會重點說一下面試
可重複讀(Repeatable Read)是否真的解決幻讀的問題!sql
開始我先提一下,根據事務的隔離級別不一樣,會有三種狀況發生。即髒讀、不可重複讀、幻讀。這裏我先不提這三種狀況的定義,後面在講隔離級別的時候會補上。 這裏,你們記住一點,根據髒讀、不可重複讀、幻讀定義來看(本身總結,官網沒有),有以下包含關係: 那麼,這張圖怎麼理解呢? 即,若是發生了髒讀,那麼不可重複讀和幻讀是必定發生的。由於拿髒讀的現象,用不可重複讀,幻讀的定義也能解釋的通。可是反過來,拿不可重複讀的現象,用髒讀的定義就不必定解釋的通了!app
假設有表tx_tb
以下,pId爲主鍵this
pId | name |
---|---|
1 | zhangsan |
其實這個從隔離名字就能夠看出來,一個事務能夠讀到另外一個事務未提交的數據!爲了便於說明,我簡單的畫圖說明! spa
如圖所示,一個事務檢索的數據被另外一個未提交的事務給修改了。 官網對髒讀定義的地址爲 https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dirty_read
其內容爲翻譯
**dirty read An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. **code
翻譯過來就是htm
檢索操做出來的數據是不可靠的,是能夠被另外一個未提交的事務修改的!
你會發現,咱們的演示結果和官網對髒讀的定義一致。根據咱們最開始的推理,若是存在髒讀,那麼不可重複讀和幻讀必定是存在的。
這個也能看的出來,一個事務能讀到另外一個事務已提交的數據!爲了便於說明,我簡單的畫圖說明!
如圖所示,一個事務檢索的數據只能被另外一個已提交的事務修改。 官網對不可重複讀定義的地址爲 https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_non_repeatable_read
其內容爲
**non-repeatable read The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime). **
翻譯過來就是
一個查詢語句檢索數據,隨後又有一個查詢語句在同一個事務中檢索數據,兩個數據應該是同樣的,可是實際狀況返回了不一樣的結果。!
ps
:做者注,這裏的不一樣結果,指的是在行不變的狀況下(專業點說,主鍵索引沒變),主鍵索引指向的磁盤上的數據內容變了。若是主鍵索引變了,好比新增一條數據或者刪除一條數據,就不是不可重複讀。
顯然,咱們這個現象符合不可重複讀的定義。下面,你們作一個思考:
這裏,我改變一下順序,先上幻讀的定義 官網對幻讀定義的地址爲 https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_phantom
phantom A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.
翻譯過來就是
在一次查詢的結果集裏出現了某一行數據,可是該數據並未出如今更早的查詢結果集裏。例如,在一次事務裏進行了兩次查詢,同時另外一個事務插入某一行或更新某一行數據後(該數據符合查詢語句裏where後的條件),並提交了!
好了,接下來上圖,你們本身評定該現象是否符合幻讀的定義
顯然,該現象是符合幻讀的定義的。同一事務的兩次相同查詢出現不一樣行。下面,你們作一個思考:
接下來講一下,爲何不少文章都產生誤傳,說是可重複讀能夠解決幻讀問題!緣由出自官網的一句話 (地址是:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-record-locks
) 原文內容以下
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 14.7.4, 「Phantom Rows」).
按照本來這句話的意思,應該是 InnoDB默認用了REPEATABLE READ。在這種狀況下,使用next-key locks解決幻讀問題! 結果估計,某個國內翻譯人員翻着翻着變成了 InnoDB默認用了REPEATABLE READ。在這種狀況下,能夠解決幻讀問題! 而後你們繼續你抄我,我抄你,結果你懂的! 顯然,漏了"使用了next-key locks!"這個條件後,意思徹底改變,咱們在該隔離級別下執行語句
select * from tx_tb where pId >= 1;
是快照讀,是不加任何鎖的,根本不能解決幻讀問題,除非你用
select * from tx_tb where pId >= 1 lock in share mode;
這樣,你就用上了next-key locks,解決了幻讀問題!
在該隔離級別下,全部的select
語句後都自動加上lock in share mode
。所以,在該隔離級別下,不管你如何進行查詢,都會使用next-key locks
。全部的select
操做均爲當前讀! OK,注意看上表紅色部分!就是由於使用了next-key locks
,innodb將PiD=1這條索引記錄,和(1,++∞)這個間隙鎖住了。其餘事務要在這個間隙上插數據,就會阻塞,從而防止幻讀發生! 有的人會說,你這第二次查詢的結果,也變了啊,明顯和第一次查詢結果不同啊?對此,我只能說,請看清楚啊。這是被本身的事務改的,不是被其餘事物修改的。這不算是幻讀,也不是不可重複讀。
上面羅裏吧嗦一大堆,最後來一個表格作總結吧,你面試答這個表就行。上面的一切是爲了這張表作準備!
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | 是 | 是 | 是 |
不可重複讀 | 否 | 是 | 是 |
可重複讀 | 否 | 否 | 是 |
串行化 | 否 | 否 | 否 |