迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。
手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。
面試開始,直入正題。
面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼?
老生常談,這個還有誰不會背的嗎?
我: 這個我知道,事務有四大特性,分別是原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability),簡稱ACID。
原子性是指事務中所有操作要麼全部成功,要麼全部失敗。
一致性是指事務執行前後,數據始終處於一致性狀態,不會出現數據丟失。
隔離性是指事務提交前的中間狀態對其他事務不可見,即相互隔離。
持久性是指事務提交後,數據的修改永久保存在資料庫中。
面試官: 嗯,回答得不錯。那你知道MySQL底層是怎麼實現事務的四大特性?
這道題有點深了,需要背會redo log、undo log、mvcc。
千萬別說不知道這幾個東西是幹嘛用的。
不但要知道,還要跟事務扯上關係。
我: 原子性是undo log實現的,一致性是由代碼邏輯層面保證的,隔離性是由mvcc實現的,持久性是基於redo log實現的。
Redo Log(重做日誌):
Redo Log記錄的是物理日誌,也就是磁碟數據的修改。
用來保證服務崩潰後,仍能把事務中變更的數據持久化到磁碟上。
如果沒有Redo Log的話,會發生什麼?
修改數據的過程就變成這樣了:
- 從磁碟載入數據到記憶體
- 在記憶體中修改數據
- 把新數據持久化到磁碟
這樣做,會有嚴重的性能問題。
- InnoDB在磁碟中存儲的基本單元是頁,可能本次修改只變更一頁中幾個位元組,但是需要刷新整頁的數據,就很浪費資源。
- 一個事務可能修改了多頁中的數據,頁之間又是不連續的,就會產生隨機IO,性能更差。
所以為了提高寫入性能,於是就引入了Redo Log。
看一下引入Redo Log後修改流程:
- 從磁碟載入數據到記憶體
- 在記憶體中修改數據
- 把新數據寫到Redo Log Buffer中
- 把Redo Log Buffer中數據持久化到Redo Log文件中
- 把Redo Log文件中數據持久化到資料庫磁碟中
Undo Log(回滾日誌):
Undo Log記錄的是邏輯日誌,用來回滾事務時,恢復到修改前的數據。
比如:當我們執行一條insert語句時,Undo Log就記錄一條相反的delete語句。
加入Undo Log之後的修改流程就是這樣的:
MVCC(多版本併發控制,Multi-Version Concurrency Control):
記錄的是某個時間點上的數據快照,用來實現不同事務之間數據的隔離性。
提到隔離性,一定要說一下事務的隔離級別。
說事務隔離級別之前,必須要先說一下併發事務產生的問題:
臟讀: 一個事務讀到其他事務未提交的數據。
不可重覆讀: 多次讀取相同的數據,得到的結果集不一致,即讀到其他事務提交後的數據。
幻讀: 相同的查詢條件,多次讀取的結果不一致,即讀到其他事務提交後的數據。
不可重覆讀與幻讀的區別是: 不可重覆讀是讀到了其他事務執行update、delete後的數據,而幻讀是讀到其他事務執行insert後的數據。
隔離級別
Read UnCommitted(讀未提交): 讀到其他事務未提交的數據,會出現臟讀、不可重覆讀、幻讀。
Read Committed(讀已提交): 讀到其他事務已提交的數據,解決了臟讀,會出現不可重覆讀、幻讀。
Repeatable Read(可重覆讀): 相同的數據,多次讀取到的結果集一致。解決了不可重覆讀,還是會出現幻讀。
Serializable(串列化): 所有事務串列執行,解決了幻讀。
再談MVCC:
MVCC解決了讀寫衝突,實現了讀寫並行,提升了事務的性能。
由於Read UnCommitted隔離級別下,每次都讀取最新的數據。而Serializable隔離級別下,對所有讀取數據都加鎖。這兩種隔離級不需要MVCC,所以MVCC只在Read Committed和Repeatable Read兩種隔離級別下起作用。
MVCC的實現方式通過兩個隱藏列trx_id(最近一次提交事務的ID)和roll_pointer(上個版本的地址),建立一個版本鏈。併在事務中讀取的時候生成一個ReadView(讀視圖),在Read Committed隔離級別下,每次讀取都會生成一個讀視圖,而在Repeatable Read隔離級別下,只會在第一次讀取時生成一個讀視圖。
InnoDB如何解決幻讀的?
先普及一下快照讀和當前讀。
當前讀: 讀取數據的最新版本,並對數據進行加鎖。
例如:insert、update、delete、select for update
快照讀: 讀取數據的歷史版本,不對數據加鎖。
例如:select
在當前讀的情況下,是通過加鎖來解決幻讀。
在快照讀的情況下,是通過MVCC來解決幻讀。
面試官: 還得是你,就你總結的全。今天的面試先到這吧,下一面要問你MySQL的鎖,你準備一下。
本文知識點總結:
文章持續更新,可以微信搜一搜「 一燈架構 」第一時間閱讀更多技術乾貨。