1. 程式人生 > >[51CTO]新說MySQL事務隔離級別!

[51CTO]新說MySQL事務隔離級別!

新說MySQL事務隔離級別!

事務隔離級別這個問題,無論是校招還是社招,面試官都愛問!然而目前網上很多文章,說句實在話啊,我看了後我都懷疑作者弄懂沒!本文所講大部分內容,皆有官網作為佐證,因此對本文內容你可以看完後,你完全可以當概念記在腦海裡,除非官網的開發手冊是錯的,否則應當無誤!

http://database.51cto.com/art/201812/589087.htm

 

引言

大家在面試中一定碰到過

說說事務的隔離級別吧?

老實說,事務隔離級別這個問題,無論是校招還是社招,面試官都愛問!然而目前網上很多文章,說句實在話啊,我看了後我都懷疑作者弄懂沒!因為他們對可重複讀(Repeatable Read)和序列化(serializable)的解析實在是看的我一頭霧水!

再加上很多書都說可重複讀解決了幻讀問題,比如《mysql技術內幕--innodb儲存引擎》等,不一一列舉了,因此網上關於事務隔離級別的文章大多是有問題的,所以再開一文說明!

本文所講大部分內容,皆有官網作為佐證,因此對本文內容你可以看完後,你完全可以當概念記在腦海裡,除非官網的開發手冊是錯的,否則應當無誤!

另外,本文會重點說一下

可重複讀(Repeatable Read)是否真的解決幻讀的問題!

正文

開始我先提一下,根據事務的隔離級別不同,會有三種情況發生。即髒讀、不可重複讀、幻讀。這裡我先不提這三種情況的定義,後面在講隔離級別的時候會補上。

這裡,大家記住一點,根據髒讀、不可重複讀、幻讀定義來看(自己總結,官網沒有),有如下包含關係:

 

那麼,這張圖怎麼理解呢?

即,如果發生了髒讀,那麼不可重複讀和幻讀是一定發生的。因為拿髒讀的現象,用不可重複讀,幻讀的定義也能解釋的通。但是反過來,拿不可重複讀的現象,用髒讀的定義就不一定解釋的通了!

假設有表tx_tb如下,pId為主鍵

 

讀未提交

即READ_UNCOMMITTED,其實這個從隔離名字就可以看出來,一個事務可以讀到另一個事務未提交的資料!為了便於說明,我簡單的畫圖說明!

 

如圖所示,一個事務檢索的資料被另一個未提交的事務給修改了。

官網對髒讀定義的地址為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.

翻譯過來就是

檢索操作出來的資料是不可靠的,是可以被另一個未提交的事務修改的!

你會發現,我們的演示結果和官網對髒讀的定義一致。根據我們最開始的推理,如果存在髒讀,那麼不可重複讀和幻讀一定是存在的。

讀已提交

即READ_COMMITTED,這個也能看的出來,一個事務能讀到另一個事務已提交的資料!為了便於說明,我簡單的畫圖說明!

 

如圖所示,一個事務檢索的資料只能被另一個已提交的事務修改。

官網對不可重複讀定義的地址為

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:作者注,這裡的不同結果,指的是在行不變的情況下(專業點說,主鍵索引沒變),但是主鍵索引指向的磁碟上的資料內容變了。如果主鍵索引變了,比如新增一條資料或者刪除一條資料,就不是不可重複讀。

顯然,我們這個現象符合不可重複讀的定義。下面,大家做一個思考:

  • 這個不可重複讀的定義,放到髒讀的現象裡是不是也可以說的通。顯然髒讀的現象,也就是讀未提交的那個例子,是不是也符合在同一個事務中返回了不同結果!
  • 但是反過來就不一定通了,一個事務A中查詢兩次的結果在被另一個事務B改變的情況下,如果事務B未提交就改變了事務A的結果,就屬於髒讀,也屬於不可重複讀。如果該事務B提交了才改變事務A的結果,就不屬於髒讀,但屬於不可重複讀。

可重複讀

即REPEATABLE_READ。這裡,我改變一下順序,先上幻讀的定義

官網對幻讀定義的地址為

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!"這個條件後,意思完全改變,我們在該隔離級別下執行語句

  1. select * from tx_tb where pId >= 1; 

是快照讀,是不加任何鎖的,根本不能解決幻讀問題,除非你用

  1. select * from tx_tb where pId >= 1 lock in share mode; 

這樣,你就用上了next-key locks,才能解決幻讀問題!

序列讀

即SERIALIZABLE_READ。在該隔離級別下,所有的select語句後都自動加上lock in share mode。因此,在該隔離級別下,無論你如何進行查詢,都會使用next-key locks。所有的select操作均為當前讀!

 

OK,注意看上表紅色部分!就是因為在該隔離級別下使用了next-key locks,innodb將pId=1這條索引記錄,和(1,++∞)這個間隙鎖住了。其他事務要在這個間隙上插資料,就會阻塞,從而防止幻讀發生!

有的人會說,你這第二次查詢的結果,也變了啊,明顯和第一次查詢結果不一樣啊?對此,我只能說,請看清楚啊。這是被自己的事務改的,不是被其他事物修改的。這不算是幻讀,也不是不可重複讀。

總結

上面羅裡吧嗦一大堆,最後來一個表格做總結吧,你面試答這個表就行。上面的一切是為了這張表做準備!