面試官靈魂拷問:MySQL 事務(wù)隔離與 MVCC,你真的懂嗎?
一、你是否也有這些痛點(diǎn)?
面試官:小徐,你在項(xiàng)目里用過事務(wù)嗎?
小徐:用過啊,Spring的@Transactional用得很熟。
面試官:那你能說說MySQL的事務(wù)隔離級別和MVCC嗎?它們是怎么保證數(shù)據(jù)一致性的?
小徐:……(沉默)
是不是很熟悉? 你是不是也遇到過這些場景:
- 明明加了事務(wù),數(shù)據(jù)還是被“臟讀”了?
- 事務(wù)隔離級別一堆名詞,讀未提交、可重復(fù)讀、幻讀,傻傻分不清楚?
- 面試官一問MVCC,腦子里只剩下“多版本并發(fā)控制”這幾個字?
別急,今天我們就用一場“靈魂拷問式”對話,把事務(wù)的隔離級別和MVCC講明白,徹底搞懂它們的底層原理和實(shí)際應(yīng)用!

二、問題1:事務(wù)是什么?
面試官:小徐,你先說說,事務(wù)是什么?
我:事務(wù)是一組操作,要么都成功,要么都失敗,用來保證數(shù)據(jù)的一致性。
面試官點(diǎn)點(diǎn)頭,又問:那事務(wù)的四大特性你還記得嗎?
我:當(dāng)然。
- 原子性(Atomicity):事務(wù)中的操作要么全部成功,要么全部失敗。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)都必須保持一致。
- 隔離性(Isolation):并發(fā)事務(wù)之間互不干擾。
- 持久性(Durability):事務(wù)提交后,結(jié)果永久保留。
面試官滿意地點(diǎn)頭:那我們就重點(diǎn)聊聊“隔離性”。你能說說MySQL支持的事務(wù)隔離級別嗎?分別會出現(xiàn)哪些并發(fā)問題?
三、問題2:什么是事務(wù)的隔離級別?
我:隔離性聽起來簡單:別人操作數(shù)據(jù)庫的時候我別看到就行了。
但真要實(shí)現(xiàn)“互不干擾”,成本非常高。所以數(shù)據(jù)庫給了我們四個等級的隔離級別,讓我們按需選擇:
隔離級別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
讀未提交(RU) | √ | √ | √ |
讀已提交(RC) | × | √ | √ |
可重復(fù)讀(RR) | × | × | √ |
串行化(SERIAL) | × | × | × |
面試官:你說說,什么是臟讀、不可重復(fù)讀和幻讀?
我:假設(shè)銀行有個賬戶A,余額是100元。
臟讀(Dirty Read):讀到了別人還沒提交的數(shù)據(jù)。

不可重復(fù)讀(Non-repeatable Read):兩次讀取結(jié)果不一致,就是不可重復(fù)讀。

幻讀(Phantom Read):T1覺得自己出現(xiàn)“幻覺”了,明明查過只有1條,怎么又冒出一條?這就叫幻讀。

面試官:MySQL 默認(rèn)是什么隔離級別?
我:MySQL 默認(rèn)是 Repeatable Read(可重復(fù)讀)隔離級別。
面試官:那你知道,MySQL 的隔離級別到底是通過什么實(shí)現(xiàn)的嗎?
我:MySQL 的隔離級別,主要是通過兩大機(jī)制來實(shí)現(xiàn)的:
- 一是鎖機(jī)制
- 二是 MVCC(多版本并發(fā)控制)。
面試官:不錯,能具體說說,什么是MVCC嗎?
四、問題3:MVCC 到底是怎么一回事?
我:MVCC,全稱 Multi-Version Concurrency Control,多版本并發(fā)控制。
通俗講:每次讀操作,都能讀到一個“歷史快照”,就像你拍了張數(shù)據(jù)的照片,別人改不影響你。
面試官:那MVCC的原理是什么?它是怎么做到讓每個事務(wù)看到自己的快照的?
1. MVCC實(shí)現(xiàn)原理
我:MVCC的實(shí)現(xiàn)主要依賴于以下幾個關(guān)鍵點(diǎn):
(1) 隱藏字段:每一行數(shù)據(jù)在InnoDB存儲引擎下,都會有兩個隱藏字段:
- 創(chuàng)建版本號(DB_TRX_ID):記錄插入或最后一次修改該行的事務(wù)ID。
- 刪除版本號(DB_ROLL_PTR):記錄刪除該行的事務(wù)ID(如果沒被刪除則為NULL)。
(2) Undo Log(回滾日志)
- 當(dāng)數(shù)據(jù)被修改(UPDATE/DELETE)時,InnoDB會把舊版本的數(shù)據(jù)寫入Undo Log。
- 這樣,其他事務(wù)如果需要讀取歷史版本的數(shù)據(jù),就可以通過Undo Log“回溯”到對應(yīng)的快照。
(3) Read View(可見性視圖)
- 每個事務(wù)在啟動時,會生成一個Read View,記錄當(dāng)前活躍的事務(wù)ID列表。
- 事務(wù)只能“看到”在自己啟動前已經(jīng)提交的數(shù)據(jù)版本。
(4) 快照讀與當(dāng)前讀
- 快照讀(Snapshot Read):普通的SELECT語句,走M(jìn)VCC,不加鎖。
- 當(dāng)前讀(Current Read):帶有鎖的操作(如SELECT ... FOR UPDATE、UPDATE、DELETE),需要讀取最新版本并加鎖。
2. MVCC的工作流程

我們通過一個例子解釋下:
(1) 事務(wù)A開始,生成快照
事務(wù)A啟動,記錄當(dāng)前數(shù)據(jù)的快照(此時name=張三,age=10)。
(2) 事務(wù)B啟動,把name從“張三”改為“李四”。
數(shù)據(jù)庫不會直接覆蓋原數(shù)據(jù),而是把原來的數(shù)據(jù)(張三)寫入undo log,生成一個新版本(李四),并更新事務(wù)ID和roll_pointer。
(3) 事務(wù)B未提交時,事務(wù)A讀取數(shù)據(jù)
- 事務(wù)A此時去讀這條數(shù)據(jù),發(fā)現(xiàn)有新版本(李四,10),但這個版本的事務(wù)ID比A大,且還沒提交。
- 根據(jù)MVCC規(guī)則,A不能看到B未提交的修改。
- 于是A會通過roll_pointer去undo log里找歷史版本,找到自己快照時的數(shù)據(jù)(張三,10),返回給A。
(4) 事務(wù)B提交:事務(wù)B提交后,新的數(shù)據(jù)版本(李四)正式生效。
(5) 事務(wù)C啟動時,快照中已經(jīng)包含了B的提交。此時C讀取數(shù)據(jù),看到的就是最新的(李四,10)。

面試官:MVCC能解決所有并發(fā)問題嗎?幻讀怎么處理?
五、問題4:MVCC能解決所有并發(fā)問題嗎?
我:MVCC不能解決所有并發(fā)問題。它主要解決了“臟讀”、“不可重復(fù)讀”等問題,讓讀寫操作互不阻塞,提高了并發(fā)性能。但MVCC無法解決“幻讀”問題。
MVCC適用的隔離級別:
- 讀已提交(RC)和可重復(fù)讀(RR):都用MVCC實(shí)現(xiàn)快照讀。
- 讀未提交(RU):不走M(jìn)VCC,直接讀最新數(shù)據(jù)。
- 串行化(SERIALIZABLE):加鎖,性能最差。
面試官點(diǎn)頭:那MySQL怎么避免幻讀?
我:MVCC本身無法徹底解決幻讀,因?yàn)镸VCC只保證了“行級”的多版本,并不能控制“新行的出現(xiàn)或消失”。
在MySQL InnoDB中,解決幻讀主要依賴于“間隙鎖(Gap Lock)”:
- 在可重復(fù)讀(REPEATABLE READ)隔離級別下,InnoDB會在范圍查詢時加上間隙鎖,防止其他事務(wù)在查詢范圍內(nèi)插入新行,從而避免幻讀。
- 如果是讀已提交(READ COMMITTED),則不會加間隙鎖,幻讀依然可能發(fā)生。
面試官:Perfect!看來你對MVCC了解的很深入了,下周就來公司報(bào)道吧。
六、總結(jié)
所謂的 MVCC,指的是在 READ COMMITTED 和 REPEATABLE READ 這兩種隔離級別下,事務(wù)在執(zhí)行普通 SELECT 操作時,通過訪問記錄的版本鏈,實(shí)現(xiàn)不同事務(wù)間的讀-寫、寫-讀操作可以并發(fā)進(jìn)行,從而提升系統(tǒng)性能。
實(shí)用建議:什么時候選哪種隔離級別?
- 如果對一致性要求極高(如轉(zhuǎn)賬系統(tǒng)):用 SERIALIZABLE,但注意性能下降嚴(yán)重
- 一般系統(tǒng)默認(rèn) REPEATABLE READ 就夠用了,配合MVCC性能好、隔離也夠
- 如果你只想防止臟讀,但可以接受不可重復(fù)讀,READ COMMITTED 可以提高并發(fā)
- 可別圖省事用最低的 READ UNCOMMITTED,臟讀太危險,幾乎沒人用。

































