文章初衷
最開始聽到隔離級別這個詞是在大學上數(shù)據(jù)庫課的時候老師講到却邓,當時聽得一頭霧水最終也沒理解到耿芹,就不了了之了怠硼。到畢業(yè)開始找工作,每次面試前的知識點整理私沮,總能看到隔離級別這個詞始赎,但是每次都停留在背書式的記住。一直對隔離級別這個詞感覺很陌生甚至有點抗拒仔燕。最近公司在做技術(shù)分享造垛,我想分享一下MVCC的理解,在查資料的過程中發(fā)現(xiàn)MVCC 跟 隔離級別晰搀、undo log等知識點關(guān)聯(lián)還是很密切的五辽,所以也順便系統(tǒng)的去學習一下隔離級別。
開始正題
我對隔離級別的理解:事務之間互相影響的程度外恕。比如在A事務中需要查詢和修改用戶余額字段奔脐,現(xiàn)在進來了一個B事務也改動了用戶余額的字段,隔離級別就規(guī)定B事務的改動對A事務的影響程度吁讨。
不同的隔離級別導致 A髓迎、B兩個事務之間的操作有不同的影響。
隔離級別可以理解為處理多事務并發(fā)操作數(shù)據(jù)時的不同策略建丧。下面先列出多事務并發(fā)操作可能引起的問題排龄。
ANSI/ISO SQL 定義了 4 種標準隔離級別。
下面根據(jù)隔離程度從低到高分別說明。
讀未提交(Read uncommitted)
這個隔離級別規(guī)定:在查詢數(shù)據(jù)時能讀取到未提交事務中修改了的數(shù)據(jù)結(jié)果橄维。
字面上理解起來可能比較難尺铣,我們直接來看例子:
時間點 | 事務A | 事務B |
---|---|---|
T1 | 開啟事務 | |
T2 | 開啟事務 | |
T3 | 查詢余額,結(jié)果是100 | |
T4 | 修改余額為150 | |
T5 | 查詢余額争舞,結(jié)果是150 | |
T6 | 提交事務 | |
T7 | 提交事務 |
上面例子可以看到困后,事務B在修改了余額后,事務A能馬上查詢到修改后的結(jié)果创译。這就是 讀未提交 的效果酒唉,結(jié)合上面這個例子去理解的話,讀未提交就是:事務A能 讀 事務B 未提交 的數(shù)據(jù)委乌。
這個隔離級別會產(chǎn)生一個問題床牧。假如事務B最終沒有提交事務,而是回滾了遭贸「昕龋可以看一下下面這個例子:
時間點 | 事務A | 事務B |
---|---|---|
T1 | 開啟事務 | |
T2 | 開啟事務 | |
T3 | 查詢余額,結(jié)果是100 | |
T4 | 修改余額為150 | |
T5 | 查詢余額壕吹,結(jié)果是150 | |
T6 | 事務回滾 | |
T7 | 查詢余額著蛙,結(jié)果是100 | |
T8 | 提交事務 |
由于事務B最終回滾了,事務A在 時間點T4 拿到的余額150是一個臟數(shù)據(jù)耳贬,我們稱這個問題為:臟讀册踩。
總結(jié)來說,在讀未提交隔離級別下效拭,數(shù)據(jù)的改動能實時的被查詢出來暂吉,這帶來的問題是會出現(xiàn) 臟讀。
讀已提交(Read commited)
理解了上面的讀未提交缎患,再來看讀已提交的話應該就比較好理解了慕的。
讀已提交隔離級別規(guī)定:只有在事務提交后,事務中修改了的數(shù)據(jù)才能被其他事務查詢到挤渔。
我們還是來看一下例子:
時間點 | 事務A | 事務B |
---|---|---|
T1 | 開啟事務 | |
T2 | 開啟事務 | |
T3 | 查詢余額肮街,結(jié)果是100 | |
T4 | 修改余額為150 | |
T5 | 查詢余額,結(jié)果是100 | |
T6 | 提交事務 | |
T7 | 查詢余額判导,結(jié)果是150 | |
T8 | 提交事務 |
根據(jù)上面這個例子可以看到嫉父,事務B 在 時間點T4 修改了余額但未提交事務,事務A 在 T5 查詢余額時眼刃,是查不到事務B修改過的余額绕辖。在事務B 提交事務后,事務A再去查詢余額擂红,就能拿到事務B 修改后的余額仪际。
結(jié)合這個例子去理解的話,讀已提交就是:事務A能 讀 事務B 已提交 的數(shù)據(jù)。
這個隔離級別不會出現(xiàn)臟讀树碱,但會有另外的問題肯适。可以看回上面的例子成榜,事務A 對余額做了多次查詢框舔,卻得到了不同的結(jié)果,在我們實現(xiàn)部分業(yè)務邏輯時這可能是一個不正常的現(xiàn)象赎婚。因為重復讀同一條數(shù)據(jù)會得到不同的結(jié)果刘绣,所以我們稱這個問題為:不可重復讀。
總結(jié)來說惑淳,在讀已提交隔離級別下额港,數(shù)據(jù)的改動在事務提交后能實時的被查詢出來饺窿,這帶來的問題是會出現(xiàn) 不可重復讀歧焦。
可重復讀(Repeatable read)
這個隔離級別名稱我認為是比較抽象的。由于這個隔離級別解決了 讀已提交 產(chǎn)生的不可重復讀問題肚医,所以這個隔離級別就叫 可重復讀绢馍。
可重復讀隔離級別規(guī)定:事務只能查詢到事務開始之前已提交的數(shù)據(jù)。
我們還是通過具體例子來說明:
時間點 | 事務A | 事務B | 事務C |
---|---|---|---|
T1 | 開啟事務 | ||
T2 | 開啟事務 | ||
T3 | 查詢余額肠套,結(jié)果是100 | ||
T4 | 修改余額為150 | ||
T5 | 查詢余額舰涌,結(jié)果是100 | ||
T6 | 提交事務 | ||
T7 | 查詢余額,結(jié)果是100 | ||
T8 | 開始事務 | ||
T9 | 查詢余額你稚,結(jié)果是150 | ||
T10 | 提交事務 | ||
T11 | 提交事務 |
事務B 在事務A 開始之后才開始瓷耙,所以事務B的更新操作不會影響到事務A 的查詢,即使事務B提交了刁赖,事務A還是拿到余額100的結(jié)果搁痛。事務C 在事務B 提交之后才開始,所以事務C 能查到事務B 修改后的結(jié)果宇弛。
可以看到事務A每次查詢都能得到同樣的結(jié)果鸡典,這解決了 讀已提交 的不可重復讀問題。
但這個隔離級別還是存在另外一個問題:幻讀枪芒。我認為 幻讀 這個問題是最難理解的彻况,我們先來看一下例子:
假設(shè)我們現(xiàn)在表中有兩條數(shù)據(jù):
id | account | balance |
---|---|---|
1 | 張三 | 1000 |
2 | 李四 | 2000 |
然后我們進行以下操作
時間點 | 事務A | 事務B |
---|---|---|
T1 | 開啟事務 | |
T2 | 開啟事務 | |
T3 | 新增一個王五的賬號,余額3000 | |
T4 | 提交事務 | |
T5 | 查詢列表舅踪,返回 張三纽甘、李四 兩條數(shù)據(jù) | |
T6 | 更新王五的余額 | |
T7 | 查詢列表,返回 張三抽碌、李四贷腕、王五 三條數(shù)據(jù) | |
T8 | 提交事務 |
上面例子時間點 T5,T6,T7 看上去很詭異,但是實際操作的確也是得到這樣的結(jié)果。我是這樣理解的:
- 事務A 在 T5 查詢不到王五的數(shù)據(jù)泽裳,那是因為事務B在事務A之后開始瞒斩,這里滿足 可重復讀 隔離級別的規(guī)則。
- T6 能更新王五這條數(shù)據(jù)應該是最讓人迷惑的涮总。但其實我們細想一下胸囱,這里是事務A 的更新操作,而我們之前談到的隔離級別都是講的select操作瀑梗。在這里事務A 通過select語句的確是查詢不到事務A 的數(shù)據(jù)烹笔,但是update 操作根據(jù)where其實是能找到這一條數(shù)據(jù)的∨桌觯可以理解為谤职,select的查詢與update中where查詢是不一樣邏輯。
- T7 這時候能查到王五這一條數(shù)據(jù)亿鲜,是因為T6 對王五做了更新操作允蜈,導致王五這條數(shù)據(jù)存在一個事務A操作過的版本≥锪可重復讀隔離級別下饶套,是允許查詢到當前事務中修改過的數(shù)據(jù),所以事務A 這里查得到王五這條數(shù)據(jù)也是符合可重復讀隔離級別的垒探。
上面這個例子妓蛮,一開始我們查詢列表只有2條數(shù)據(jù),后來又查到3條數(shù)據(jù)圾叼,這就是我們上面提到的 幻讀蛤克。
幻讀 與 不可重復讀 有那么一點相像,兩者都是在用相同條件多次查詢時得到不同的結(jié)果夷蚊。我的理解是构挤,不可重復讀更側(cè)重于數(shù)據(jù)內(nèi)容的變更,而幻讀側(cè)重于由于事務內(nèi)的一些操作導致本來查詢不到的數(shù)據(jù)變成對當前事務可見撬码。
串行化(Serializable)
這是最嚴謹?shù)母綦x級別儿倒,為了不因為多事務并行執(zhí)行導致數(shù)據(jù)的不一致,直接規(guī)定事務串行去執(zhí)行呜笑。就是上一個事務未結(jié)束夫否,下一個事務不會開始。串行化比較好理解叫胁,就不做舉例了凰慈。
前面提到的3種問題(臟讀、不可重復度驼鹅、幻讀)在串行化隔離級別下微谓,都不會發(fā)生森篷,所以說這個是最嚴謹?shù)母綦x級別。
隔離級別越高豺型,需要消耗的性能越多仲智。
MySQL InnoDB引擎默認使用 可重復讀 隔離級別
這里額外說一點,讀已提交姻氨、可重復讀 這兩個隔離級別的數(shù)據(jù)其實是比較特別的钓辆。我們再拿可重復讀中的例子來看一下:
時間點 | 事務A | 事務B | 事務C |
---|---|---|---|
T1 | 開啟事務 | ||
T2 | 開啟事務 | ||
T3 | 查詢余額,結(jié)果是100 | ||
T4 | 修改余額為150 | ||
T5 | 查詢余額肴焊,結(jié)果是100 | ||
T6 | 提交事務 | ||
T7 | 查詢余額前联,結(jié)果是100 | ||
T8 | 開始事務 | ||
T9 | 查詢余額,結(jié)果是150 | ||
T10 | 提交事務 | ||
T11 | 提交事務 |
這個例子中娶眷,事務A查到的余額一直是100似嗤,而事務B在修改余額后,事務B查詢余額結(jié)果會是150届宠。不知道大家在這里會不會產(chǎn)生疑問烁落,同一個賬戶的余額,事務A查的到是100席揽,事務B查到的是150顽馋,那數(shù)據(jù)庫中這個賬戶的余額字段存的究竟是存的100還是150谓厘?還是100和150余額同時存在于數(shù)據(jù)庫幌羞?這其實是一個叫做 多版本并發(fā)控制 的知識點,又稱作 MVCC(Multi-Version Concurrency Control)竟稳。上面例子中属桦,余額為100和150 的數(shù)據(jù)是同時存在的,這就是多版本的意思他爸。具體的 MVCC介紹留到下一篇文章再說了聂宾。