文章初衷
最開(kāi)始聽(tīng)到隔離級(jí)別這個(gè)詞是在大學(xué)上數(shù)據(jù)庫(kù)課的時(shí)候老師講到,當(dāng)時(shí)聽(tīng)得一頭霧水最終也沒(méi)理解到,就不了了之了。到畢業(yè)開(kāi)始找工作,每次面試前的知識(shí)點(diǎn)整理,總能看到隔離級(jí)別這個(gè)詞,但是每次都停留在背書(shū)式的記住。一直對(duì)隔離級(jí)別這個(gè)詞感覺(jué)很陌生甚至有點(diǎn)抗拒。最近公司在做技術(shù)分享,我想分享一下MVCC的理解,在查資料的過(guò)程中發(fā)現(xiàn)MVCC 跟 隔離級(jí)別、undo log等知識(shí)點(diǎn)關(guān)聯(lián)還是很密切的,所以也順便系統(tǒng)的去學(xué)習(xí)一下隔離級(jí)別。
開(kāi)始正題
我對(duì)隔離級(jí)別的理解:事務(wù)之間互相影響的程度。比如在A事務(wù)中需要查詢(xún)和修改用戶(hù)余額字段,現(xiàn)在進(jìn)來(lái)了一個(gè)B事務(wù)也改動(dòng)了用戶(hù)余額的字段,隔離級(jí)別就規(guī)定B事務(wù)的改動(dòng)對(duì)A事務(wù)的影響程度。
不同的隔離級(jí)別導(dǎo)致 A、B兩個(gè)事務(wù)之間的操作有不同的影響。
隔離級(jí)別可以理解為處理多事務(wù)并發(fā)操作數(shù)據(jù)時(shí)的不同策略。下面先列出多事務(wù)并發(fā)操作可能引起的問(wèn)題。
ANSI/ISO SQL 定義了 4 種標(biāo)準(zhǔn)隔離級(jí)別。
下面根據(jù)隔離程度從低到高分別說(shuō)明。
讀未提交(Read uncommitted)
這個(gè)隔離級(jí)別規(guī)定:在查詢(xún)數(shù)據(jù)時(shí)能讀取到未提交事務(wù)中修改了的數(shù)據(jù)結(jié)果。
字面上理解起來(lái)可能比較難,我們直接來(lái)看例子:
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B |
|---|---|---|
| T1 | 開(kāi)啟事務(wù) | |
| T2 | 開(kāi)啟事務(wù) | |
| T3 | 查詢(xún)余額,結(jié)果是100 | |
| T4 | 修改余額為150 | |
| T5 | 查詢(xún)余額,結(jié)果是150 | |
| T6 | 提交事務(wù) | |
| T7 | 提交事務(wù) |
上面例子可以看到,事務(wù)B在修改了余額后,事務(wù)A能馬上查詢(xún)到修改后的結(jié)果。這就是 讀未提交 的效果,結(jié)合上面這個(gè)例子去理解的話(huà),讀未提交就是:事務(wù)A能 讀 事務(wù)B 未提交 的數(shù)據(jù)。
這個(gè)隔離級(jí)別會(huì)產(chǎn)生一個(gè)問(wèn)題。假如事務(wù)B最終沒(méi)有提交事務(wù),而是回滾了??梢钥匆幌孪旅孢@個(gè)例子:
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B |
|---|---|---|
| T1 | 開(kāi)啟事務(wù) | |
| T2 | 開(kāi)啟事務(wù) | |
| T3 | 查詢(xún)余額,結(jié)果是100 | |
| T4 | 修改余額為150 | |
| T5 | 查詢(xún)余額,結(jié)果是150 | |
| T6 | 事務(wù)回滾 | |
| T7 | 查詢(xún)余額,結(jié)果是100 | |
| T8 | 提交事務(wù) |
由于事務(wù)B最終回滾了,事務(wù)A在 時(shí)間點(diǎn)T4 拿到的余額150是一個(gè)臟數(shù)據(jù),我們稱(chēng)這個(gè)問(wèn)題為:臟讀。
總結(jié)來(lái)說(shuō),在讀未提交隔離級(jí)別下,數(shù)據(jù)的改動(dòng)能實(shí)時(shí)的被查詢(xún)出來(lái),這帶來(lái)的問(wèn)題是會(huì)出現(xiàn) 臟讀。
讀已提交(Read commited)
理解了上面的讀未提交,再來(lái)看讀已提交的話(huà)應(yīng)該就比較好理解了。
讀已提交隔離級(jí)別規(guī)定:只有在事務(wù)提交后,事務(wù)中修改了的數(shù)據(jù)才能被其他事務(wù)查詢(xún)到。
我們還是來(lái)看一下例子:
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B |
|---|---|---|
| T1 | 開(kāi)啟事務(wù) | |
| T2 | 開(kāi)啟事務(wù) | |
| T3 | 查詢(xún)余額,結(jié)果是100 | |
| T4 | 修改余額為150 | |
| T5 | 查詢(xún)余額,結(jié)果是100 | |
| T6 | 提交事務(wù) | |
| T7 | 查詢(xún)余額,結(jié)果是150 | |
| T8 | 提交事務(wù) |
根據(jù)上面這個(gè)例子可以看到,事務(wù)B 在 時(shí)間點(diǎn)T4 修改了余額但未提交事務(wù),事務(wù)A 在 T5 查詢(xún)余額時(shí),是查不到事務(wù)B修改過(guò)的余額。在事務(wù)B 提交事務(wù)后,事務(wù)A再去查詢(xún)余額,就能拿到事務(wù)B 修改后的余額。
結(jié)合這個(gè)例子去理解的話(huà),讀已提交就是:事務(wù)A能 讀 事務(wù)B 已提交 的數(shù)據(jù)。
這個(gè)隔離級(jí)別不會(huì)出現(xiàn)臟讀,但會(huì)有另外的問(wèn)題??梢钥椿厣厦娴睦?,事務(wù)A 對(duì)余額做了多次查詢(xún),卻得到了不同的結(jié)果,在我們實(shí)現(xiàn)部分業(yè)務(wù)邏輯時(shí)這可能是一個(gè)不正常的現(xiàn)象。因?yàn)橹貜?fù)讀同一條數(shù)據(jù)會(huì)得到不同的結(jié)果,所以我們稱(chēng)這個(gè)問(wèn)題為:不可重復(fù)讀。
總結(jié)來(lái)說(shuō),在讀已提交隔離級(jí)別下,數(shù)據(jù)的改動(dòng)在事務(wù)提交后能實(shí)時(shí)的被查詢(xún)出來(lái),這帶來(lái)的問(wèn)題是會(huì)出現(xiàn) 不可重復(fù)讀。
可重復(fù)讀(Repeatable read)
這個(gè)隔離級(jí)別名稱(chēng)我認(rèn)為是比較抽象的。由于這個(gè)隔離級(jí)別解決了 讀已提交 產(chǎn)生的不可重復(fù)讀問(wèn)題,所以這個(gè)隔離級(jí)別就叫 可重復(fù)讀。
可重復(fù)讀隔離級(jí)別規(guī)定:事務(wù)只能查詢(xún)到事務(wù)開(kāi)始之前已提交的數(shù)據(jù)。
我們還是通過(guò)具體例子來(lái)說(shuō)明:
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B | 事務(wù)C |
|---|---|---|---|
| T1 | 開(kāi)啟事務(wù) | ||
| T2 | 開(kāi)啟事務(wù) | ||
| T3 | 查詢(xún)余額,結(jié)果是100 | ||
| T4 | 修改余額為150 | ||
| T5 | 查詢(xún)余額,結(jié)果是100 | ||
| T6 | 提交事務(wù) | ||
| T7 | 查詢(xún)余額,結(jié)果是100 | ||
| T8 | 開(kāi)始事務(wù) | ||
| T9 | 查詢(xún)余額,結(jié)果是150 | ||
| T10 | 提交事務(wù) | ||
| T11 | 提交事務(wù) |
事務(wù)B 在事務(wù)A 開(kāi)始之后才開(kāi)始,所以事務(wù)B的更新操作不會(huì)影響到事務(wù)A 的查詢(xún),即使事務(wù)B提交了,事務(wù)A還是拿到余額100的結(jié)果。事務(wù)C 在事務(wù)B 提交之后才開(kāi)始,所以事務(wù)C 能查到事務(wù)B 修改后的結(jié)果。
可以看到事務(wù)A每次查詢(xún)都能得到同樣的結(jié)果,這解決了 讀已提交 的不可重復(fù)讀問(wèn)題。
但這個(gè)隔離級(jí)別還是存在另外一個(gè)問(wèn)題:幻讀。我認(rèn)為 幻讀 這個(gè)問(wèn)題是最難理解的,我們先來(lái)看一下例子:
假設(shè)我們現(xiàn)在表中有兩條數(shù)據(jù):
| id | account | balance |
|---|---|---|
| 1 | 張三 | 1000 |
| 2 | 李四 | 2000 |
然后我們進(jìn)行以下操作
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B |
|---|---|---|
| T1 | 開(kāi)啟事務(wù) | |
| T2 | 開(kāi)啟事務(wù) | |
| T3 | 新增一個(gè)王五的賬號(hào),余額3000 | |
| T4 | 提交事務(wù) | |
| T5 | 查詢(xún)列表,返回 張三、李四 兩條數(shù)據(jù) | |
| T6 | 更新王五的余額 | |
| T7 | 查詢(xún)列表,返回 張三、李四、王五 三條數(shù)據(jù) | |
| T8 | 提交事務(wù) |
上面例子時(shí)間點(diǎn) T5,T6,T7 看上去很詭異,但是實(shí)際操作的確也是得到這樣的結(jié)果。我是這樣理解的:
- 事務(wù)A 在 T5 查詢(xún)不到王五的數(shù)據(jù),那是因?yàn)槭聞?wù)B在事務(wù)A之后開(kāi)始,這里滿(mǎn)足 可重復(fù)讀 隔離級(jí)別的規(guī)則。
- T6 能更新王五這條數(shù)據(jù)應(yīng)該是最讓人迷惑的。但其實(shí)我們細(xì)想一下,這里是事務(wù)A 的更新操作,而我們之前談到的隔離級(jí)別都是講的select操作。在這里事務(wù)A 通過(guò)select語(yǔ)句的確是查詢(xún)不到事務(wù)A 的數(shù)據(jù),但是update 操作根據(jù)where其實(shí)是能找到這一條數(shù)據(jù)的。可以理解為,select的查詢(xún)與update中where查詢(xún)是不一樣邏輯。
- T7 這時(shí)候能查到王五這一條數(shù)據(jù),是因?yàn)門(mén)6 對(duì)王五做了更新操作,導(dǎo)致王五這條數(shù)據(jù)存在一個(gè)事務(wù)A操作過(guò)的版本??芍貜?fù)讀隔離級(jí)別下,是允許查詢(xún)到當(dāng)前事務(wù)中修改過(guò)的數(shù)據(jù),所以事務(wù)A 這里查得到王五這條數(shù)據(jù)也是符合可重復(fù)讀隔離級(jí)別的。
上面這個(gè)例子,一開(kāi)始我們查詢(xún)列表只有2條數(shù)據(jù),后來(lái)又查到3條數(shù)據(jù),這就是我們上面提到的 幻讀。
幻讀 與 不可重復(fù)讀 有那么一點(diǎn)相像,兩者都是在用相同條件多次查詢(xún)時(shí)得到不同的結(jié)果。我的理解是,不可重復(fù)讀更側(cè)重于數(shù)據(jù)內(nèi)容的變更,而幻讀側(cè)重于由于事務(wù)內(nèi)的一些操作導(dǎo)致本來(lái)查詢(xún)不到的數(shù)據(jù)變成對(duì)當(dāng)前事務(wù)可見(jiàn)。
串行化(Serializable)
這是最嚴(yán)謹(jǐn)?shù)母綦x級(jí)別,為了不因?yàn)槎嗍聞?wù)并行執(zhí)行導(dǎo)致數(shù)據(jù)的不一致,直接規(guī)定事務(wù)串行去執(zhí)行。就是上一個(gè)事務(wù)未結(jié)束,下一個(gè)事務(wù)不會(huì)開(kāi)始。串行化比較好理解,就不做舉例了。
前面提到的3種問(wèn)題(臟讀、不可重復(fù)度、幻讀)在串行化隔離級(jí)別下,都不會(huì)發(fā)生,所以說(shuō)這個(gè)是最嚴(yán)謹(jǐn)?shù)母綦x級(jí)別。
隔離級(jí)別越高,需要消耗的性能越多。
MySQL InnoDB引擎默認(rèn)使用 可重復(fù)讀 隔離級(jí)別
這里額外說(shuō)一點(diǎn),讀已提交、可重復(fù)讀 這兩個(gè)隔離級(jí)別的數(shù)據(jù)其實(shí)是比較特別的。我們?cè)倌每芍貜?fù)讀中的例子來(lái)看一下:
| 時(shí)間點(diǎn) | 事務(wù)A | 事務(wù)B | 事務(wù)C |
|---|---|---|---|
| T1 | 開(kāi)啟事務(wù) | ||
| T2 | 開(kāi)啟事務(wù) | ||
| T3 | 查詢(xún)余額,結(jié)果是100 | ||
| T4 | 修改余額為150 | ||
| T5 | 查詢(xún)余額,結(jié)果是100 | ||
| T6 | 提交事務(wù) | ||
| T7 | 查詢(xún)余額,結(jié)果是100 | ||
| T8 | 開(kāi)始事務(wù) | ||
| T9 | 查詢(xún)余額,結(jié)果是150 | ||
| T10 | 提交事務(wù) | ||
| T11 | 提交事務(wù) |
這個(gè)例子中,事務(wù)A查到的余額一直是100,而事務(wù)B在修改余額后,事務(wù)B查詢(xún)余額結(jié)果會(huì)是150。不知道大家在這里會(huì)不會(huì)產(chǎn)生疑問(wèn),同一個(gè)賬戶(hù)的余額,事務(wù)A查的到是100,事務(wù)B查到的是150,那數(shù)據(jù)庫(kù)中這個(gè)賬戶(hù)的余額字段存的究竟是存的100還是150?還是100和150余額同時(shí)存在于數(shù)據(jù)庫(kù)?這其實(shí)是一個(gè)叫做 多版本并發(fā)控制 的知識(shí)點(diǎn),又稱(chēng)作 MVCC(Multi-Version Concurrency Control)。上面例子中,余額為100和150 的數(shù)據(jù)是同時(shí)存在的,這就是多版本的意思。具體的 MVCC介紹留到下一篇文章再說(shuō)了。