?我當(dāng)面試官,面你?MySQL?原理
時間:2021-10-25 15:31:32
手機(jī)看文章
掃描二維碼
隨時隨地手機(jī)看文章
[導(dǎo)讀]言歸正傳,之前MySQL基礎(chǔ)篇的知識點(diǎn),小伙伴們有熟練掌握嗎?沒有的話趕緊來復(fù)習(xí)一下~我當(dāng)面試官,面你MySQL基礎(chǔ)覺得已經(jīng)沒有問題,我們就一起來進(jìn)行第二部分的學(xué)習(xí)吧——MySQL原理篇?;A(chǔ)篇主要是側(cè)重基礎(chǔ)知識,原理篇是有一定基礎(chǔ)后的遞進(jìn),通過學(xué)習(xí)本篇,不僅可以進(jìn)一步了解MyS...
言歸正傳,之前MySQL基礎(chǔ)篇的知識點(diǎn),小伙伴們有熟練掌握嗎?沒有的話趕緊來復(fù)習(xí)一下~
- 我當(dāng)面試官,面你 MySQL 基礎(chǔ)
基礎(chǔ)篇主要是側(cè)重基礎(chǔ)知識,原理篇是有一定基礎(chǔ)后的遞進(jìn),通過學(xué)習(xí)本篇,不僅可以進(jìn)一步了解MySQL的各項特性,還能為接下來的容災(zāi)調(diào)優(yōu)打下堅實(shí)的基礎(chǔ)。
現(xiàn)在,就讓我們繼續(xù)跟隨阿柴進(jìn)行這場沉浸式面試吧。
ACID與隔離級別
那你先來說說MySQL的四種隔離級別吧。SQL標(biāo)準(zhǔn)定義了4類隔離級別,包括一些具體規(guī)則,用來限定事務(wù)之間的隔離性。
這四種級別分別是讀未提交、讀已提交、可重復(fù)讀、串型化。
讀未提交,顧名思義,就是可以讀到還沒有提交的數(shù)據(jù);讀已提交會讀到其它事務(wù)已經(jīng)提交的數(shù)據(jù);可重復(fù)讀確保了同一事務(wù)中,讀取同一條數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行;串型化通過強(qiáng)制事務(wù)排序,使其不可能相互沖突。
重點(diǎn)介紹下Repeatable Read吧。Repeatable Read就是可重復(fù)讀。它確保了在同一事務(wù)中,讀取同一條數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行。
它也是MyQL的默認(rèn)事務(wù)隔離級別,這種級別事務(wù)之間影響很小,通常已經(jīng)能夠滿足日常需要了。
說出四種隔離級別只是最低要求,能每一項具體去闡述特性就算過關(guān)。如果還能指出存在的問題、依賴的技術(shù),那么就是妥妥的加分了!
下面我們來聊聊InnoDB中ACID的實(shí)現(xiàn)吧,先說一下原子性是怎么實(shí)現(xiàn)的。事務(wù)要么失敗,要么成功,不能做一半。聰明的InnoDB,在干活兒之前,先將要做的事情記錄到一個叫undo log的日志文件中,如果失敗了或者主動rollback,就可以通過undo log的內(nèi)容,將事務(wù)回滾。
那undo log里面具體記錄了什么信息呢?undo log屬于邏輯日志,它記錄的是sql執(zhí)行相關(guān)的信息。當(dāng)發(fā)生回滾時,InnoDB會根據(jù)undo log的內(nèi)容做與之前相反的工作,使數(shù)據(jù)回到之前的狀態(tài)。。。
那持久性又是怎么實(shí)現(xiàn)的?持久性是用來保證一旦給客戶返回成功,數(shù)據(jù)就不會消失,持久存在。最簡單的做法,是每次寫完磁盤落地之后,再給客戶返回成功。但如果每次讀寫數(shù)據(jù)都需要磁盤IO,效率就會很低。
為此,追求極致的InnoDB提供了緩沖。當(dāng)向數(shù)據(jù)庫寫入數(shù)據(jù)時,會首先寫入緩沖池,緩沖池中修改的數(shù)據(jù)會定期刷新到磁盤中,這一過程稱為刷臟。
如果MySQL宕機(jī),那此時Buffer Pool中修改的數(shù)據(jù)不是丟失了嗎?Innodb引入了redo log來解決這個問題。當(dāng)數(shù)據(jù)修改時,會先在redo log記錄這次操作,然后再修改緩沖池中的數(shù)據(jù),當(dāng)事務(wù)提交時,會調(diào)用fsync接口對redo log進(jìn)行刷盤。
如果MySQL宕機(jī),重啟時可以讀取redo log中的數(shù)據(jù),對數(shù)據(jù)庫進(jìn)行恢復(fù)。由于redo log是WAL日志,也就是預(yù)寫式日志,所有修改先寫入日志,所以保證了數(shù)據(jù)不會因MySQL宕機(jī)而丟失,從而滿足了持久性要求。
按你所說,redo log 也需要寫磁盤,為什么不直接將數(shù)據(jù)寫磁盤呢?嗯。。。主要是有以下兩方面的原因:
1.對Buffer Pool進(jìn)行刷臟是隨機(jī)IO,因為每次修改的數(shù)據(jù)位置隨機(jī),但寫redo log是追加操作,屬于順序IO;
2.刷臟是以數(shù)據(jù)頁為單位,MySQL默認(rèn)頁大小是16KB,一個Page上一個小修改都要整頁寫入,所以積累一些數(shù)據(jù)一并寫入會大大提升性能;而redo log中只包含真正需要寫入的部分,無效IO比較少。
redo log是持久性的核心,WAL的思路也是持久化的常見解決方式,只有先落地了,才能應(yīng)對后續(xù)的各種異常。
那隔離性怎么實(shí)現(xiàn)呢?MySQL能支持Repeatable Read這種高隔離級別,主要是鎖和MVCC一起努力的結(jié)果。
我先說鎖吧。事務(wù)在讀取某數(shù)據(jù)的瞬間,必須先對其加行級共享鎖,直到事務(wù)結(jié)束才釋放;事務(wù)在更新某數(shù)據(jù)的瞬間,必須先對其加行級排他鎖,直到事務(wù)結(jié)束才釋放;
為了防止幻讀,還會有間隙鎖進(jìn)行區(qū)間排它鎖定。
然后是MVCC,多版本并發(fā)控制,主要是為了實(shí)現(xiàn)可重復(fù)讀,雖然鎖也可以,但是為了更高性能考慮,使用了這種多版本快照的方式。
因為是快照,所以一個事務(wù)針對同一條Sql查詢語句的結(jié)果,不會受其它事務(wù)影響。
索引原理
索引的底層實(shí)現(xiàn)是什么?用的B 樹,它是一個N叉排序樹,每個節(jié)點(diǎn)通常有多個子節(jié)點(diǎn)。節(jié)點(diǎn)種類有普通節(jié)點(diǎn)和葉子節(jié)點(diǎn)。根節(jié)點(diǎn)可能是一個葉子節(jié)點(diǎn), 也可能是個普通節(jié)點(diǎn)。
B 樹
那MySQL為什么用樹做索引?一般而言,能做索引的,要么Hash,要么樹,要么就是比較特殊的跳表。Hash不支持范圍查詢,跳表不適合這種磁盤場景,而樹支持范圍查詢,且多種多樣,很多樹適合磁盤存儲。所以MySQL選擇了樹來做索引。
那你能說說為什么是B 樹,而不是平衡二叉樹、紅黑樹或者B-樹嗎?平衡二叉樹追求絕對平衡,條件比較苛刻,實(shí)現(xiàn)起來比較麻煩,每次插入新節(jié)點(diǎn)之后需要旋轉(zhuǎn)的次數(shù)不能預(yù)知。
同時,B 樹優(yōu)勢在于每個節(jié)點(diǎn)能存儲多個信息,這樣深度比平衡二叉樹會淺很多,減少數(shù)據(jù)查找的次數(shù)。
平衡二叉樹
紅黑樹放棄了追求完全平衡,只追求大致平衡,在與平衡二叉樹的時間復(fù)雜度相差不大的情況下,保證每次插入最多只需要三次旋轉(zhuǎn)就能達(dá)到平衡,實(shí)現(xiàn)起來也更為簡單。
但是紅黑樹多用于內(nèi)部排序,即全放在內(nèi)存中,而B 樹多用于外存上時,B 也被稱為一個磁盤友好的數(shù)據(jù)結(jié)構(gòu)。
同時,紅黑樹和平衡二叉樹有相同缺點(diǎn),即每個節(jié)點(diǎn)存儲一個關(guān)鍵詞,數(shù)據(jù)量大時,導(dǎo)致它們的深度很深,MySQL每次讀取時都會消耗大量IO。
紅黑樹
那B 樹相比B-樹有什么優(yōu)點(diǎn)呢?哈哈,我覺得這就屬于同門師兄較勁兒了。B 樹非葉子節(jié)點(diǎn)只存儲key值,而B-樹存儲key值和data值,這樣B 樹的層級更少,查詢效率更高;
MySQL進(jìn)行區(qū)間訪問時,由于B 樹葉子節(jié)點(diǎn)之間用指針相連,只需要遍歷所有的葉子節(jié)點(diǎn)即可,而B-樹則需要中序遍歷一遍。
B-樹
這類選型問題其實(shí)很深,要深刻理解為什么要用B 樹、B 樹有哪些競爭對手。換句話說,也就是要了解,哪些數(shù)據(jù)結(jié)構(gòu)能做索引。如果能答出哈希表、樹、跳表這三大類,就說明確實(shí)有自己的深入思考,這部分知識點(diǎn)學(xué)透了,也是加分項。
接下來講講聚簇索引和二級索引吧。聚簇索引是主鍵上的索引,二級索引是非主鍵字段的索引。這兩者相同點(diǎn)是都是基于B 樹實(shí)現(xiàn)。
區(qū)別在于,二級索引的葉子結(jié)點(diǎn)只存儲索引本身內(nèi)容,以及主鍵ID,聚簇索引的葉子結(jié)點(diǎn),會存儲完整的行數(shù)據(jù)。在一定程度上,可以說二級索引就是主鍵索引的索引。
一般來說,面試官讓介紹兩個名詞或者概念,潛臺詞就是要我們說清楚兩者的相同點(diǎn)、不同點(diǎn),說清楚了就過關(guān)。如果有些自己的總結(jié)性思考,比如在上面的對話中,阿柴回答出二級索引是主鍵索引的索引,這樣就會讓面試官眼前一亮。
鎖
下面講講MySQL鎖的分類吧。MySQL從鎖粒度粒度上講,有表級鎖、行級鎖。從強(qiáng)度上講,又分為意向共享鎖、共享鎖、意向排它鎖和排它鎖。
鎖模式的兼容情況
那select操作會加鎖嗎?對于普通select語句,InnoDB 不會加任何鎖。但是select語句,也可以顯示指定加鎖。有兩種模式,一種是LOCK IN SHARE MODE是加共享鎖,還有Select ... for updates是加排它鎖。
什么情況下會發(fā)生死鎖?嗯。。。比如事務(wù)A鎖住了資源1,然后去申請資源2,但事務(wù)B已經(jīng)占據(jù)了資源2,需要資源1,誰都不退讓,就死鎖了。對于MySQL,最常見的情況,就是資源1、資源2分別對應(yīng)一個排它鎖。
那間隙鎖你有了解么?間隙鎖就是對索引行進(jìn)行加鎖操作,不僅鎖住其本身,還會鎖住周圍鄰近的范圍區(qū)間。間隙鎖的目的是為了解決幻影讀,但也因此帶來了更大的死鎖隱患。
比如,一個任務(wù)表里面有個狀態(tài)字段,是一個非唯一索引,有一個任務(wù)id,是唯一索引。
一個sql將狀態(tài)處于執(zhí)行中的任務(wù)設(shè)置為等待中,另一個sql正好通過任務(wù)id更新在范圍內(nèi)的一條任務(wù)信息。那么因為是在不同索引加鎖的,所以都能成功。但是最后去更新主鍵數(shù)據(jù)的時候,就會死鎖。
介于篇幅,其中的一些知識點(diǎn),比如MVCC,并未擴(kuò)展出來深度闡述,建議大家可以看看我往期的數(shù)據(jù)庫文章。
- 索引為什么能提高查詢效率
- 事務(wù)、事務(wù)隔離級別和MVCC
- MySQL 全局鎖、表級鎖、行級鎖
- MySQL 到底是怎么加行鎖的?
- 林哥,幻讀是怎么被解決的?
- 完蛋,公司被一條 update 語句干趴了!