面试必会系列 - 2.1 MySQL知识点大汇总(基本架构,存储引擎,锁,事务,索引,B+树等等)
本文已收錄至 Github(MD-Notes),若博客中圖片模糊或打不開(kāi),可以來(lái)我的 Github 倉(cāng)庫(kù),包含了完整圖文:https://github.com/HanquanHq/MD-Notes,涵蓋了互聯(lián)網(wǎng)大廠(chǎng)面試必問(wèn)的知識(shí)點(diǎn),講解透徹,長(zhǎng)期更新中,歡迎一起學(xué)習(xí)探討 ~
更多內(nèi)容,可以訪(fǎng)問(wèn):
面試必會(huì)系列專(zhuān)欄:https://blog.csdn.net/sinat_42483341/category_10300357.html
操作系統(tǒng)系列專(zhuān)欄:https://blog.csdn.net/sinat_42483341/category_10519484.html
目錄
- MySQL
- MySQL 基本架構(gòu)
- 連接器
- 查詢(xún)緩存
- 分析器
- 優(yōu)化器
- 執(zhí)行器
- 存儲(chǔ)引擎
- 零碎知識(shí)點(diǎn)
- 局部性原理
- 時(shí)間、空間局部性
- 磁盤(pán)預(yù)讀
- MySQL 日志有多少種?
- Undolog 回滾日志
- Redolog 物理日志
- undolog 的原理?是否需要落盤(pán)?
- MySQL有多少種鎖?
- 使用自定義變量
- 分區(qū)表
- 存儲(chǔ)引擎
- 事務(wù)
- 事務(wù)的 ACID
- 事務(wù)的實(shí)現(xiàn)原理
- Atomicity 原子性:undolog
- Consistency 一致性(數(shù)據(jù)庫(kù)的根本追求)
- Isolation 隔離性
- Durability 持久性:redolog + binlog
- 思想:WAL日志(Write Ahead Log,預(yù)寫(xiě)日志)
- 采用 redo log 的好處?
- 三種數(shù)據(jù)溢寫(xiě)到磁盤(pán)的過(guò)程
- 數(shù)據(jù)更新的流程?redo的兩階段提交
- 數(shù)據(jù)更新的執(zhí)行流程
- 鎖
- MyIsam
- Innodb
- OLTP,OLAP
- MySQL 索引實(shí)現(xiàn)原理
- 不同存儲(chǔ)引擎的數(shù)據(jù)文件
- 聚簇索引就是主鍵索引嗎?
- Innodb 采用自適應(yīng)哈希:
- 擾動(dòng)函數(shù)(Java HashMap相關(guān)的,自己看一下吧)
- MySQL B+ 樹(shù)數(shù)據(jù)結(jié)構(gòu)推導(dǎo)
- 哈希表
- 普通二叉樹(shù)
- BST Tree(二叉排序樹(shù))
- AVL 樹(shù)(二叉平衡樹(shù))
- RBTree (紅黑樹(shù))
- 為什么使用 B / B+ 樹(shù)?
- 為什么推薦使用自增的 int 類(lèi)型作為主鍵?
- 不同存儲(chǔ)引擎的數(shù)據(jù)結(jié)構(gòu)
- B 樹(shù)
- B+ 樹(shù)
- 索引分類(lèi)
- 1、按照索引的存儲(chǔ)來(lái)劃分:簇族索引、非簇族索引
- 2、按照使用來(lái)分:
- 回表 & 覆蓋索引
- 索引下推
- 1、沒(méi)有索引下推的情況
- 2、有索引下推的情況
- 3、總結(jié)
MySQL
MySQL 基本架構(gòu)
連接器
負(fù)責(zé)和客戶(hù)端建立連接,獲取權(quán)限,維持和管理連接
- 用戶(hù)名密碼驗(yàn)證
- 查詢(xún)權(quán)限信息,分配對(duì)應(yīng)的權(quán)限
- 可以使用show processlist查看現(xiàn)有的連接
- wait_timeout默認(rèn)8小時(shí),超時(shí)會(huì)斷開(kāi)連接
連接分為兩類(lèi)
- 長(zhǎng)連接:推薦使用,但是要周期性的斷開(kāi)長(zhǎng)連接
- 短鏈接:一次執(zhí)行完畢就關(guān)閉,比較消耗資源
查詢(xún)緩存
- 查詢(xún)緩存失效比較頻繁,只要表更新,緩存就會(huì)清空
- 緩存對(duì)應(yīng)更新的市局命中率低
分析器
優(yōu)化器
執(zhí)行具體的SQL之前先進(jìn)行優(yōu)化
- 索引優(yōu)化
- 條件順序優(yōu)化
- 關(guān)聯(lián)表順序優(yōu)化
- …
不同的執(zhí)行方式對(duì)效率影響很大
- RBO:基于規(guī)則的優(yōu)化
- CBO:基于成本的優(yōu)化
執(zhí)行器
操作引擎,返回結(jié)果
存儲(chǔ)引擎
存儲(chǔ)數(shù)據(jù),提供讀寫(xiě)接口
零碎知識(shí)點(diǎn)
局部性原理
時(shí)間、空間局部性
數(shù)據(jù)和程序的存儲(chǔ),都有聚集成群的傾向,相關(guān)關(guān)聯(lián)的數(shù)據(jù)可能被放在一起。同時(shí),之前查詢(xún)過(guò)的數(shù)據(jù),短時(shí)間內(nèi)可能再次被查詢(xún)。
磁盤(pán)預(yù)讀
當(dāng)內(nèi)存和磁盤(pán)發(fā)生交互的時(shí)候,是以一個(gè)邏輯單元 “頁(yè)” 為單位進(jìn)行交互的,“頁(yè)”是磁盤(pán)和內(nèi)存交互的最小單位,一般是 4k 或 8k。讀取的時(shí)候可以以頁(yè)為單位,也可以是頁(yè)的整數(shù)倍。
SSD 4K 對(duì)齊,能夠加快查詢(xún)效率
MySQL 日志有多少種?
binlog, undolog, redolog, relaylog(主從復(fù)制), errorlog, slowlog 等
-
所有存儲(chǔ)引擎,都有 binlog,errorlog,relaylog,slowlog
-
Innodb 存儲(chǔ)引擎,有 binlog, undolog, redolog
-
MyISAM 不支持事務(wù),沒(méi)有 undolog, redolog,只有 binlog
Undolog 回滾日志
Redolog 物理日志
innodb存儲(chǔ)引擎的日志文件。
-
redolog是物理日志,記錄的是在某個(gè)數(shù)據(jù)頁(yè)上做了什么修改
- 當(dāng)發(fā)生數(shù)據(jù)修改的時(shí)候,innodb存儲(chǔ)引擎會(huì)先將記錄寫(xiě)到redo_log中,并更新內(nèi)存,此時(shí)更新就算是完成了,同時(shí)INNODB會(huì)在合適的時(shí)機(jī)將記錄存儲(chǔ)操做到磁盤(pán)中。
- redo_log是由固定大小的,是一個(gè)循環(huán)寫(xiě)的過(guò)程
- 有了redo_log之后,innodb可以保證數(shù)據(jù)庫(kù)異常之后重啟,之前的數(shù)據(jù)記錄不會(huì)丟失,叫做crash-safe
-
binlog是邏輯日志,記錄的是這個(gè)語(yǔ)句的原始邏輯,比如給ID=2這一行的c字段加1;
有且僅有兩個(gè)文件,是一個(gè)循環(huán)寫(xiě)的過(guò)程。
不知道你是否記得《孔乙己》這篇文章,酒店掌柜有一個(gè)粉板,專(zhuān)門(mén)記錄客人的賒賬記錄。如果賒賬的人不多,他可以將賒賬的人姓名和賬目寫(xiě)在板上,但是如果賒賬的人太多,粉板總會(huì)有記不下的時(shí)候,這時(shí)候掌柜還有一個(gè)專(zhuān)門(mén)記錄賒賬的賬本。
如果有人要賒賬或者還賬的時(shí)候,掌柜一般有兩種方法:
1、一種直接將賬本翻出來(lái),把這次賬加上或者刪除
2、先在粉板上記下這次賬,等打烊后再把賬本翻出來(lái)核算
在生意很忙時(shí),掌柜應(yīng)該選擇后者,第一種方法實(shí)在太麻煩了,極大的影響工作效率。
同樣,在MySQL里也有這個(gè)問(wèn)題,如果每一次更新操作都寫(xiě)進(jìn)磁盤(pán),然后磁盤(pán)找到對(duì)應(yīng)的那條記錄,然后再更新,整個(gè)過(guò)程的IO成本,查找成本都很高。為了解決這個(gè)問(wèn)題,MySQL的設(shè)計(jì)者就用了類(lèi)似酒店掌柜粉板的思路來(lái)提升工作效率。
粉板和賬本配合的過(guò)程,其實(shí)就是MySQL里面經(jīng)常說(shuō)到的WAL技術(shù),WAL技術(shù)全稱(chēng)是Write-Ahead Logging.他的關(guān)鍵點(diǎn)就是先寫(xiě)日志,再寫(xiě)磁盤(pán)。
具體來(lái)說(shuō),當(dāng)有一條日志需要更新的時(shí)候。InnoDB 會(huì)先把日志寫(xiě)到 redo log(粉板)中,并更新內(nèi)存,這個(gè)時(shí)候更新就算完成了。同時(shí),InnoDB 會(huì)在適當(dāng)?shù)臅r(shí)候?qū)⑦@個(gè)操作記錄到磁盤(pán)中,這個(gè)更新往往實(shí)在系統(tǒng)比較空閑的時(shí)候,這就像打樣以后掌柜做的事。
如果今天賒賬的不多,掌柜可以打烊后再整理,但是某天賒賬的非常多,粉板寫(xiě)滿(mǎn)了,又怎么辦呢?這個(gè)時(shí)候掌柜只好放下手中的事,將粉板上的賬整理到賬本上,再將粉板擦掉,為記錄新的賒賬騰出空間。
與此類(lèi)似,InnoDB 的 redo log 是固定大小的,比如可以配置為一組四個(gè)文件,每個(gè)文件的大小是1G,那么這塊粉板共有4G的空間。從頭開(kāi)始寫(xiě),寫(xiě)到末尾又回到開(kāi)頭循環(huán)寫(xiě),如下圖所示:
write pos 是當(dāng)前記錄的位置,一邊寫(xiě)一邊后移,寫(xiě)到第3號(hào)文件的末尾就回到0號(hào)文件開(kāi)頭。checkpoint 是當(dāng)前要擦除的位置,也是往后推移并且魂環(huán)的,擦除記錄前要將記錄更新到數(shù)據(jù)文件。
wirte 和 checkpoint 之間是粉板空著的部分,可以用來(lái)記錄新的操作。如果 write pos 追上 checkpoint,表示粉板滿(mǎn)了,這個(gè)時(shí)候就不能執(zhí)行新的更新操作,要先停下來(lái)擦掉一些記錄,把checkpoint推進(jìn)一下。
有了redo-log,InnoDB就可以保證即使數(shù)據(jù)庫(kù)發(fā)生異常重啟,之前提交的記錄就不會(huì)丟失。這個(gè)能力成為crash-safe。
要理解crash-safe這個(gè)概念,可以想想賒賬的例子。只要賒賬記錄記在粉板上或者寫(xiě)在賬本上,之后即使掌柜忘記了,比如停業(yè)幾天,恢復(fù)生意后依然可以通過(guò)賬本和粉板上的數(shù)據(jù)明確賒賬數(shù)目。
undolog 的原理?是否需要落盤(pán)?
innodb通過(guò)force log at commit機(jī)制實(shí)現(xiàn)事務(wù)的持久性,即在事務(wù)提交的時(shí)候,必須先將該事務(wù)的所有事務(wù)日志寫(xiě)入到磁盤(pán)上的 redo log file 和 undo log file 中,進(jìn)行持久化。
undo日志會(huì)記錄事務(wù)執(zhí)行過(guò)程中,每次修改的數(shù)據(jù)的原始值。
x = 5, y = 8 t1 begin:// undo日志記錄x=5x = x - 1;// undo日志記錄y=8y = y - 2;// 事務(wù)執(zhí)行臨近結(jié)束,將 undolog 寫(xiě)入到磁盤(pán)// 將數(shù)據(jù)寫(xiě)入到磁盤(pán) commit每次進(jìn)行事務(wù)修改之前,把未修改之前的值存儲(chǔ)到 undo 日志中,提交的時(shí)候,先將 undo 寫(xiě)到磁盤(pán),再把修改后的數(shù)據(jù)寫(xiě)到磁盤(pán)。
若undo寫(xiě)入磁盤(pán)之前發(fā)生了異常,根本就不需要做任何操作,這時(shí)候事務(wù)是被認(rèn)為執(zhí)行失敗的,也不需要回滾,因?yàn)閡ndo日志沒(méi)有寫(xiě)入磁盤(pán),數(shù)據(jù)庫(kù)被認(rèn)為處于沒(méi)有執(zhí)行事務(wù)的狀態(tài)。
MySQL有多少種鎖?
共享鎖,排它鎖,獨(dú)占鎖,間隙鎖,臨鍵鎖,自增鎖,意向鎖
MVCC:multi version concurrency control 多版本并發(fā)控制,通過(guò)保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來(lái)實(shí)現(xiàn)的。在同一個(gè)事務(wù)里能夠看到數(shù)據(jù)一致的視圖。
排它鎖怎么加?query for update
共享鎖怎么加?lock in share mode
WAL:Write Ahead Log 溢寫(xiě)日志
使用自定義變量
在給一個(gè)變量賦值的同時(shí),使用這個(gè)變量
select actor_id, @rounum:=@rownum+1 as rownum from actor limit 10;分區(qū)表
創(chuàng)建表時(shí)使用 partition by 子句定義每個(gè)分區(qū)存放的數(shù)據(jù),在執(zhí)行查詢(xún)的時(shí)候,優(yōu)化器會(huì)根據(jù)分區(qū)定義過(guò)濾那些沒(méi)有我們需要數(shù)據(jù)的分區(qū),這樣查詢(xún)就無(wú)須掃描所有分區(qū)。
存儲(chǔ)引擎
- innodb
- 有 redolog, undolog
- 簇族索引
- myisam
- 非簇族索引
- 不支持事務(wù)
- memory
- 數(shù)據(jù)在內(nèi)存中,有持久化文件
- 默認(rèn)使用哈希索引
事務(wù)
- 數(shù)據(jù)庫(kù)事務(wù)
- spring 聲明式事務(wù):spring 提供了一個(gè)類(lèi),由這個(gè)類(lèi)以 AOP 的方式管理,只需要@Transactional即可
- 分布式事務(wù)
事務(wù)的 ACID
事務(wù)的實(shí)現(xiàn)原理
事務(wù)的原子性,是通過(guò) undo log 來(lái)實(shí)現(xiàn)的
事務(wù)的持久性,是通過(guò) redo log 來(lái)實(shí)現(xiàn)的
事務(wù)的隔離性,是通過(guò) (讀寫(xiě)鎖+MVCC)來(lái)實(shí)現(xiàn)的
事務(wù)的一致性,是通過(guò)原子性,持久性,隔離性來(lái)實(shí)現(xiàn)的!!!
Atomicity 原子性:undolog
innodb 默認(rèn)頁(yè) 16k
-
事務(wù)中的所有操作作為一個(gè)整體,像原子一樣不可分割(原子性),要么全部執(zhí)行成功,要么全部失敗
-
使用 undolog 邏輯日志實(shí)現(xiàn)回滾
-
Undo Log 是為了實(shí)現(xiàn)事務(wù)的原子性,在 MySQL 數(shù)據(jù)庫(kù) InnoDB 存儲(chǔ)引擎中,還用 Undo Log 來(lái)實(shí)現(xiàn) MVCC 多版本并發(fā)控制,記錄原來(lái)數(shù)據(jù)的歷史版本
-
在操作任何數(shù)據(jù)之前,首先將數(shù)據(jù)備份到一個(gè)地方(這個(gè)存儲(chǔ)數(shù)據(jù)備份的地方稱(chēng)為Undo Log)。然后進(jìn)行數(shù)據(jù)的修改。如果出現(xiàn)了錯(cuò)誤或者用戶(hù)執(zhí)行了ROLLBACK語(yǔ)句,系統(tǒng)可以利用Undo Log中的備份將數(shù)據(jù)恢復(fù)到事務(wù)開(kāi)始之前的狀態(tài)
注意:undo log 是邏輯日志,可以理解為(僅理解,實(shí)際并不是這樣的):
(區(qū)分邏輯日志、物理日志,只需要看頁(yè)是否被修改。邏輯日志 是只對(duì)當(dāng)前的 sql 語(yǔ)句做一條記錄,而 物理日志 是對(duì)日志所在物理頁(yè) page 做修改)當(dāng)delete一條記錄時(shí),undo log中會(huì)記錄一條對(duì)應(yīng)的insert記錄
當(dāng)insert一條記錄時(shí),undo log中會(huì)記錄一條對(duì)應(yīng)的delete記錄
當(dāng)update一條記錄時(shí),它記錄一條對(duì)應(yīng)相反的update記錄如果某一次操作失敗了,就去執(zhí)行這些相反的邏輯語(yǔ)句,將數(shù)據(jù)恢復(fù)到上一次的一致性狀態(tài)。
-
Consistency 一致性(數(shù)據(jù)庫(kù)的根本追求)
一致性分類(lèi):強(qiáng)一致性、弱一致性、最終一致性
在事務(wù)的四個(gè)特點(diǎn)中,一致性是事務(wù)的根本追求。事務(wù)執(zhí)行的結(jié)果必須使數(shù)據(jù)庫(kù)從 一個(gè)永久的一致性狀態(tài) 轉(zhuǎn)變到 另一個(gè)永久的一致性狀態(tài)。如果事務(wù)被迫中斷,不應(yīng)該有一部分被寫(xiě)入物理數(shù)據(jù)庫(kù)。例如,轉(zhuǎn)賬前后,兩個(gè)賬戶(hù)的總金額應(yīng)該保持不變。而在某些情況下,會(huì)對(duì)事務(wù)的一致性造成破壞:
-
事務(wù)的并發(fā)執(zhí)行
-
事務(wù)故障或系統(tǒng)故障
數(shù)據(jù)庫(kù)系統(tǒng)通過(guò)并發(fā)控制技術(shù)和日志恢復(fù)技術(shù),來(lái)避免這種情況的發(fā)生
-
并發(fā)控制技術(shù)保證了事務(wù)的隔離性,使數(shù)據(jù)庫(kù)的一致性狀態(tài)不會(huì)因?yàn)椴l(fā)執(zhí)行的操作被破壞。
-
日志恢復(fù)技術(shù)保證了事務(wù)的原子性,使一致性狀態(tài)不會(huì)因事務(wù)或系統(tǒng)故障被破壞。同時(shí)使已提交的對(duì)數(shù)據(jù)庫(kù)的修改不會(huì)因系統(tǒng)崩潰而丟失,保證了事務(wù)的持久性。
Isolation 隔離性
-
使用 鎖機(jī)制 實(shí)現(xiàn)
-
并發(fā)環(huán)境中,并發(fā)的事務(wù)是相互隔離的,并發(fā)執(zhí)行的事務(wù)之間不能相互干擾
-
隔離級(jí)別:假設(shè) A,B 都開(kāi)啟了事務(wù)
- 讀未提交(未授權(quán)讀取):即使A事務(wù)未提交,B事務(wù)也能看到A的修改
- 讀已提交(授權(quán)讀取):A事務(wù)提交后,B事務(wù)中才能看到A的修改
- 可重復(fù)讀:無(wú)論A怎么修改,事務(wù)B在事務(wù)期間都不會(huì)看到A的修改
- 串行化:所有事物只能一個(gè)接一個(gè)處理,不能并發(fā)執(zhí)行
(要能夠模擬臟讀、幻讀、不可重復(fù)讀的情況)
Durability 持久性:redolog + binlog
我們知道,寫(xiě)數(shù)據(jù)的時(shí)候,數(shù)據(jù)會(huì)先存在用戶(hù)空間內(nèi)存中,然后由操作系統(tǒng)內(nèi)核調(diào)用 fsync,才真正寫(xiě)入到磁盤(pán)。如果此時(shí)突然宕機(jī),內(nèi)存中的數(shù)據(jù)就會(huì)丟失。怎么解決這個(gè)問(wèn)題?
事務(wù)提交前直接把數(shù)據(jù)寫(xiě)入磁盤(pán)就行啊。這么做有什么問(wèn)題?只修改一個(gè)頁(yè)面里的一個(gè)字節(jié),就要將整個(gè)頁(yè)面刷入磁盤(pán),太浪費(fèi)資源了。畢竟一個(gè)頁(yè)面16kb大小,你只改其中一點(diǎn)點(diǎn)東西,就要將16kb的內(nèi)容刷入磁盤(pán),聽(tīng)著也不合理。畢竟一個(gè)事務(wù)里的SQL可能牽涉到多個(gè)數(shù)據(jù)頁(yè)的修改,而這些數(shù)據(jù)頁(yè)可能不是相鄰的,也就是屬于隨機(jī)IO。顯然操作隨機(jī)IO,速度會(huì)比較慢。
思想:WAL日志(Write Ahead Log,預(yù)寫(xiě)日志)
采用 redo log 解決上面的問(wèn)題。當(dāng)做數(shù)據(jù)修改的時(shí)候,不僅在內(nèi)存中操作,還會(huì)在redo log中記錄這次操作。當(dāng)事務(wù)提交的時(shí)候,將redo log日志進(jìn)行刷盤(pán)持久化即可(redo log一部分在內(nèi)存中,一部分在磁盤(pán)上),不需要將數(shù)據(jù)持久化。當(dāng)數(shù)據(jù)庫(kù)宕機(jī)重啟的時(shí)候,雖然數(shù)據(jù)沒(méi)有持久化,但是可以根據(jù) redo log 中的內(nèi)容,將數(shù)據(jù)恢復(fù)到數(shù)據(jù)庫(kù)中,再根據(jù) undo log 和 binlog 內(nèi)容決定回滾數(shù)據(jù)還是提交數(shù)據(jù)。
采用 redo log 的好處?
redo log 進(jìn)行刷盤(pán)比對(duì)數(shù)據(jù)頁(yè)刷盤(pán)效率高
- redo log體積小,畢竟只記錄了哪一頁(yè)修改了啥,因此體積小,刷盤(pán)快。
- redo log是一直往末尾進(jìn)行追加,屬于順序IO。效率顯然比隨機(jī)IO來(lái)的快。
- 事務(wù)一旦提交,數(shù)據(jù)必須永久保存。即使宕機(jī),重啟后也能恢復(fù)到事務(wù)成功結(jié)束時(shí)的狀態(tài)
- 使用 redolog 兩階段提交實(shí)現(xiàn)。事務(wù)提交前,需要將 redolog 持久化。系統(tǒng)崩潰時(shí),雖然數(shù)據(jù)沒(méi)有持久化,但是可以根據(jù) redolog 的內(nèi)容,將數(shù)據(jù)恢復(fù)到最新的狀態(tài)。
- redolog 大小是固定的,相當(dāng)于一個(gè)增量存儲(chǔ),redolog 滿(mǎn)了之后,會(huì)進(jìn)行持久化的同步歸檔。然后將redolog清空。
三種數(shù)據(jù)溢寫(xiě)到磁盤(pán)的過(guò)程
數(shù)據(jù)更新的流程?redo的兩階段提交
事實(shí)分析先寫(xiě)redolog后寫(xiě)binlog和先寫(xiě)binlog后寫(xiě)redolog都會(huì)有數(shù)據(jù)不一致的風(fēng)險(xiǎn)。
因此,采用兩階段提交,具體流程如下:
數(shù)據(jù)更新的執(zhí)行流程
執(zhí)行器先從存儲(chǔ)引擎找到數(shù)據(jù),如果在內(nèi)存中直接返回,不在內(nèi)存中查詢(xún)返回
執(zhí)行器拿到數(shù)據(jù)后會(huì)先修改數(shù)據(jù),然后調(diào)用引擎接口重新吸入數(shù)據(jù)
引擎將數(shù)據(jù)更新到內(nèi)存,同時(shí)寫(xiě)數(shù)據(jù)到redo中,此時(shí)處于prepare階段,并通知執(zhí)行器執(zhí)行完成
執(zhí)行器生成這個(gè)操作的binlog
執(zhí)行器調(diào)用引擎的事務(wù)提交接口,引擎把剛寫(xiě)完的redo改為commit狀態(tài)
更新完成
使用 兩階段提交的優(yōu)勢(shì) 是:可以保證 binlog 和 redolog 的數(shù)據(jù)一致(先寫(xiě) redolog 或者先寫(xiě) binlog 都無(wú)法保證突然宕機(jī)時(shí)的數(shù)據(jù)一致性)。如果數(shù)據(jù)庫(kù)發(fā)生了意外情況,宕機(jī)、斷點(diǎn)、重啟等等,可以保證使用 BinLog 恢復(fù)數(shù)據(jù)和當(dāng)時(shí)數(shù)據(jù)狀態(tài)一致。具體情況下的策略如下:
- binlog有記錄,redolog狀態(tài)commit:正常完成的事務(wù),不需要恢復(fù)
- binlog有記錄,redolog狀態(tài)prepare:在binlog寫(xiě)完提交事務(wù)之前的crash,恢復(fù)操作:提交事務(wù)
- binlog無(wú)記錄,redolog狀態(tài)prepare:在binlog寫(xiě)完之前的crash,恢復(fù)操作:回滾事務(wù)
- binlog無(wú)記錄,redolog無(wú)記錄:在redolog寫(xiě)之前crash,恢復(fù)操作:回滾事務(wù)
鎖
- 共享鎖
- 排它鎖
- 獨(dú)占鎖
- 臨鍵鎖
- 間隙鎖
- 自增鎖
- 意向鎖
MyIsam
只能鎖表
- 共享讀鎖
- 獨(dú)占寫(xiě)鎖
Innodb
支持表鎖,行鎖。實(shí)質(zhì)上鎖的是索引,如果沒(méi)有索引的話(huà),退化成為表鎖。
- 共享鎖(s),又稱(chēng)讀鎖
- 排它鎖(x),又稱(chēng)寫(xiě)鎖
OLTP,OLAP
OLTP:聯(lián)機(jī)事務(wù)處理,在盡可能短的時(shí)間內(nèi)返回對(duì)應(yīng)的結(jié)果值。例如我們常用的關(guān)系型數(shù)據(jù)庫(kù)。
OLAP:聯(lián)機(jī)分析處理,Hive,主要是對(duì)歷史數(shù)據(jù)的分析,用于做出決策;常用于數(shù)據(jù)倉(cāng)庫(kù)。不支持范圍查詢(xún),插入新數(shù)據(jù)要重排?
區(qū)別在于時(shí)效性,在很短的時(shí)間內(nèi)返回結(jié)果。
MySQL 索引實(shí)現(xiàn)原理
索引是和存儲(chǔ)引擎相關(guān)聯(lián)的。所謂存儲(chǔ)引擎,指的是數(shù)據(jù)在磁盤(pán)上的不同組織形式。
Memory 存儲(chǔ)引擎使用 Hash 索引。
不同存儲(chǔ)引擎的數(shù)據(jù)文件
I nnodb:包括 frm(表結(jié)構(gòu)),ibd(索引+數(shù)據(jù)放在一起,聚簇索引) 文件
MyISAM:包括 frm,myd,myi 文件,非聚簇索引
聚簇索引就是主鍵索引嗎?
不一定是。
- 如果你建表時(shí)不指定主鍵,innodb會(huì)選擇 唯一鍵 創(chuàng)建索引。
- 如果沒(méi)有唯一鍵的話(huà),會(huì)生成一個(gè) 6 字節(jié)的 row_id 作為主鍵。
Innodb 采用自適應(yīng)哈希:
當(dāng)給 colA 建 立B+tree 索引的時(shí)候,這棵 B+ tree 會(huì)有個(gè)三四層,通過(guò) colA = ‘xxx’ 會(huì)在樹(shù)里查詢(xún) 3、4 次才能查到,所以這里如果開(kāi)啟了自適應(yīng)索引,就利用 buffer pool 來(lái)給 colA 建立一個(gè)哈希索引,這樣就只用在哈希索引里查 1 次,不用在 B+ tree 里查詢(xún) 3、4 次,加快了速度。
擾動(dòng)函數(shù)(Java HashMap相關(guān)的,自己看一下吧)
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }目的是為了減少hash沖突。
MySQL B+ 樹(shù)數(shù)據(jù)結(jié)構(gòu)推導(dǎo)
哈希表
哈希算法應(yīng)該你可能多的案列,讓數(shù)據(jù)分布均勻,使用擾動(dòng)函數(shù),減少hash沖突;對(duì)內(nèi)存占用比較高;檢索時(shí)無(wú)法進(jìn)行范圍查詢(xún),如果范圍查詢(xún),必須逐個(gè)對(duì)比,相當(dāng)耗費(fèi)時(shí)間。
MySQL用到了哈希表嗎?Memory 存儲(chǔ)引擎使用的索引數(shù)據(jù)結(jié)構(gòu)就是哈希表;Innodb使用自適應(yīng)哈希。
普通二叉樹(shù)
查詢(xún)效率太低,需要遍歷整個(gè)樹(shù)
BST Tree(二叉排序樹(shù))
有序,左子樹(shù)<根節(jié)點(diǎn)<右子樹(shù),遞增插入會(huì)退化成鏈表,是因?yàn)闃?shù)不夠平衡
AVL 樹(shù)(二叉平衡樹(shù))
最短子樹(shù)和最長(zhǎng)子樹(shù)高度之差不能超過(guò)1,是嚴(yán)格意義上的平衡樹(shù),在插入數(shù)據(jù)的時(shí)候要進(jìn)行旋轉(zhuǎn)操作來(lái)保證平衡,會(huì)損失部分插入性能,從而帶來(lái)查詢(xún)性能的提升
RBTree (紅黑樹(shù))
非嚴(yán)格的平衡樹(shù),最長(zhǎng)路徑不超過(guò)最短路徑的兩倍。近似取得了插入和查詢(xún)性能的平衡。
為什么使用 B / B+ 樹(shù)?
以上的“二叉”樹(shù)都會(huì)越來(lái)越深,每一個(gè)節(jié)點(diǎn)中只能存一個(gè)元素。如果數(shù)據(jù)節(jié)點(diǎn)很多,查找的時(shí)候,需要進(jìn)行多次 IO 交互。應(yīng)該盡量在 4k 中存儲(chǔ)盡可能多的數(shù)據(jù)節(jié)點(diǎn)。
B / B+ 樹(shù)的每一個(gè)節(jié)點(diǎn)中可以有多個(gè)元素,采用有序、多分支的方式,解決二叉樹(shù)的這些弊端。
為什么推薦使用自增的 int 類(lèi)型作為主鍵?
int 類(lèi)型 相比 varchar,占用的索引空間比較小
自增可以直接追加在最后面,減少樹(shù)的頁(yè)分裂、合并帶來(lái)的維護(hù)成本
不同存儲(chǔ)引擎的數(shù)據(jù)結(jié)構(gòu)
Innodb 默認(rèn)使用 B-tree,根據(jù)官網(wǎng)文檔,Memory tables 也支持哈希索引。
Hash劣勢(shì):rehash,哈希沖突問(wèn)題。不好的hash算法導(dǎo)致散列不均勻,浪費(fèi)磁盤(pán)空間。
jdk 1.8 的哈希函數(shù)算法使用了擾動(dòng)函數(shù),也是為了讓散列更均勻
| B-Tree索引 | 支持 | 支持 | 支持 |
| HASH索引 | 不支持 | 不支持 | 支持 |
| R-Tree索引 | 支持 | 不支持 | 不支持 |
| Full-text索引 | 支持 | 不支持 | 不支持 |
B 樹(shù)
實(shí)例圖說(shuō)明:
B+ 樹(shù)
每個(gè)節(jié)點(diǎn)可以包含多個(gè)元素,有 n 棵子樹(shù)的節(jié)點(diǎn)中含有 n 個(gè)關(guān)鍵字。每個(gè)關(guān)鍵字不保存數(shù)據(jù),只用來(lái)索引。
非葉子結(jié)點(diǎn)只存儲(chǔ) key,不存儲(chǔ)數(shù)據(jù)。所有 數(shù)據(jù)都放在葉子結(jié)點(diǎn) 中存儲(chǔ)。是為文件系統(tǒng)而生的。
B+Tree是在B Tree的基礎(chǔ)之上做的一種優(yōu)化,變化如下:
索引分類(lèi)
1、按照索引的存儲(chǔ)來(lái)劃分:簇族索引、非簇族索引
聚簇索引:innodb 數(shù)據(jù)和索引放在一起。如果不設(shè)主鍵,innodb 會(huì)選擇一個(gè)唯一鍵,如果沒(méi)有唯一鍵,innodb會(huì)生成一個(gè) 6 字節(jié)的 rowid 存儲(chǔ),對(duì)用戶(hù)是不可見(jiàn)的。因此,聚簇索引不一定是主鍵索引。
非聚簇索引:數(shù)據(jù)和索引不放在一起,myisam
2、按照使用來(lái)分:
主鍵索引:主鍵所關(guān)聯(lián)的數(shù)據(jù)
唯一索引:mysql 默認(rèn)會(huì)給唯一鍵添加索引
普通索引:用來(lái)加速數(shù)據(jù)訪(fǎng)問(wèn)速度而建立的索引。多建立在經(jīng)常出現(xiàn)在查詢(xún)條件的字段和經(jīng)常用于排序的字段。普通索引是非聚簇索引,葉子存放的是對(duì)應(yīng)主鍵id值。
另外,如果主鍵是創(chuàng)建表之后才添加的,新建立的主鍵的索引使用的不是主鍵索引,而是在葉子上去關(guān)聯(lián)原來(lái)默認(rèn)的 rowid。因此,innodb 的主鍵索引也不一定是聚簇索引。
回表 & 覆蓋索引
回表:通過(guò)普通索引去樹(shù)中查找,會(huì) 返回主鍵值,再 **根據(jù)主鍵 **去索引樹(shù)查找數(shù)據(jù)。
select id, age from test where name = '張三';覆蓋索引:執(zhí)行計(jì)劃能看到 using index。通過(guò)檢索索引就可以讀取想要的數(shù)據(jù),那就不需要再到數(shù)據(jù)表中讀取行了。也就是不需要回表。
select id, name from test where name = '張三';索引下推
假設(shè)有這么個(gè)需求,查詢(xún)表中“名字第一個(gè)字是張,性別男,年齡為10歲的所有記錄”。那么,查詢(xún)語(yǔ)句是這么寫(xiě)的:
mysq> select * from tuser where name like '張%' and age=10 and ismale=1;根據(jù)前面說(shuō)的“最左前綴原則”,該語(yǔ)句在搜索索引樹(shù)的時(shí)候,只能匹配到名字第一個(gè)字是‘張’的記錄(即記錄ID3),接下來(lái)是怎么處理的呢?當(dāng)然就是從ID3開(kāi)始,逐個(gè)回表,到主鍵索引上找出相應(yīng)的記錄,再比對(duì)age和ismale這兩個(gè)字段的值是否符合。
但是!MySQL 5.6引入了索引下推優(yōu)化,可以在索引遍歷過(guò)程中,對(duì)索引中包含的字段先做判斷,過(guò)濾掉不符合條件的記錄,減少回表字?jǐn)?shù)。
1、沒(méi)有索引下推的情況
圖 1 中,在 (name,age) 索引里面,我特意去掉了 age 的值,因?yàn)?這個(gè)過(guò)程 InnoDB 并不會(huì)去看 age 的值,只是按順序把“name 第一個(gè)字是’張’”的記錄一條條取出來(lái)回表。因此,需要回表 4 次。
2、有索引下推的情況
圖 2 跟圖 1 的區(qū)別是,InnoDB 在 (name,age) 索引內(nèi)部就判斷了 age 是否等于 10,對(duì)于不等于 10 的記錄,直接判斷并跳過(guò)。在我們的這個(gè)例子中,只需要對(duì) ID4、ID5 這兩條記錄回表取數(shù)據(jù)判斷,就只需要回表 2 次。
3、總結(jié)
如果沒(méi)有索引下推優(yōu)化(或稱(chēng)ICP優(yōu)化),當(dāng)進(jìn)行索引查詢(xún)時(shí),首先根據(jù)索引來(lái)查找記錄,然后再根據(jù)where條件來(lái)過(guò)濾記錄;在支持ICP優(yōu)化后,MySQL會(huì)在取出索引的同時(shí),判斷是否可以進(jìn)行where條件過(guò)濾再進(jìn)行索引查詢(xún),也就是說(shuō)提前執(zhí)行where的部分過(guò)濾操作,在某些場(chǎng)景下,可以大大減少回表次數(shù),從而提升整體性能。
總結(jié)
以上是生活随笔為你收集整理的面试必会系列 - 2.1 MySQL知识点大汇总(基本架构,存储引擎,锁,事务,索引,B+树等等)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 面试必会系列 - 1.8 Spring
- 下一篇: 左神算法:分别用递归和非递归方式实现二叉