MySQL索引15連問,抗??!
?前言
大家好,我是田螺。
金三銀四很快就要來啦,準(zhǔn)備了索引的15連問,相信大家看完肯定會(huì)有幫助的。
1. 索引是什么?
- 索引是一種能提高數(shù)據(jù)庫查詢效率的數(shù)據(jù)結(jié)構(gòu)。它可以比作一本字典的目錄,可以幫你快速找到對(duì)應(yīng)的記錄。
- 索引一般存儲(chǔ)在磁盤的文件中,它是占用物理空間的。
- 正所謂水能載舟,也能覆舟。適當(dāng)?shù)乃饕芴岣卟樵冃?,過多的索引會(huì)影響數(shù)據(jù)庫表的插入和更新功能。
2. MySQL索引有哪些類型
數(shù)據(jù)結(jié)構(gòu)維度
- B+樹索引:所有數(shù)據(jù)存儲(chǔ)在葉子節(jié)點(diǎn),復(fù)雜度為O(logn),適合范圍查詢。
- 哈希索引: 適合等值查詢,檢索效率高,一次到位。
- 全文索引:MyISAM和InnoDB中都支持使用全文索引,一般在文本類型char,text,varchar類型上創(chuàng)建。
- R-Tree?索引: 用來對(duì)GIS數(shù)據(jù)類型創(chuàng)建SPATIAL索引
物理存儲(chǔ)維度
- 聚集索引:聚集索引就是以主鍵創(chuàng)建的索引,在葉子節(jié)點(diǎn)存儲(chǔ)的是表中的數(shù)據(jù)。(Innodb存儲(chǔ)引擎)
- 非聚集索引:非聚集索引就是以非主鍵創(chuàng)建的索引,在葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵和索引列。(Innodb存儲(chǔ)引擎)
邏輯維度
- 主鍵索引:一種特殊的唯一索引,不允許有空值。
- 普通索引:MySQL中基本索引類型,允許空值和重復(fù)值。
- 聯(lián)合索引:多個(gè)字段創(chuàng)建的索引,使用時(shí)遵循最左前綴原則。
- 唯一索引:索引列中的值必須是唯一的,但是允許為空值。
- 空間索引:MySQL5.7之后支持空間索引,在空間索引這方面遵循OpenGIS幾何數(shù)據(jù)模型規(guī)則。
3. 索引什么時(shí)候會(huì)失效?
- 查詢條件包含or,可能導(dǎo)致索引失效
- 如果字段類型是字符串,where時(shí)一定用引號(hào)括起來,否則索引失效
- like通配符可能導(dǎo)致索引失效。
- 聯(lián)合索引,查詢時(shí)的條件列不是聯(lián)合索引中的第一個(gè)列,索引失效。
- 在索引列上使用 mysql 的內(nèi)置函數(shù),索引失效。
- 對(duì)索引列運(yùn)算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >,not in)時(shí),可能會(huì)導(dǎo)致索引失效。
- 索引字段上使用is null, is not null,可能導(dǎo)致索引失效。
- 左連接查詢或者右連接查詢查詢關(guān)聯(lián)的字段編碼格式不一樣,可能導(dǎo)致索引失效。
- mysql 估計(jì)使用全表掃描要比使用索引快,則不使用索引。
4. 哪些場景不適合建立索引?
- 數(shù)據(jù)量少的表,不適合加索引
- 更新比較頻繁的也不適合加索引
- 區(qū)分度低的字段不適合加索引(如性別)
- where、group by、order by等后面沒有使用到的字段,不需要建立索引
- 已經(jīng)有冗余的索引的情況(比如已經(jīng)有a,b的聯(lián)合索引,不需要再單獨(dú)建立a索引)
5. 為什么要用 B+樹,為什么不用二叉樹?
可以從幾個(gè)維度去看這個(gè)問題,查詢是否夠快,效率是否穩(wěn)定,存儲(chǔ)數(shù)據(jù)多少, 以及查找磁盤次數(shù),為什么不是二叉樹,為什么不是平衡二叉樹,為什么不是 B 樹,而偏偏是 B+樹呢?
為什么不是一般二叉樹?
如果二叉樹特殊化為一個(gè)鏈表,相當(dāng)于全表掃描。平衡二叉樹相比于二叉查找 樹來說,查找效率更穩(wěn)定,總體的查找速度也更快。
為什么不是平衡二叉樹呢?
我們知道,在內(nèi)存比在磁盤的數(shù)據(jù),查詢效率快得多。如果樹這種數(shù)據(jù)結(jié)構(gòu)作 為索引,那我們每查找一次數(shù)據(jù)就需要從磁盤中讀取一個(gè)節(jié)點(diǎn),也就是我們說 的一個(gè)磁盤塊,但是平衡二叉樹可是每個(gè)節(jié)點(diǎn)只存儲(chǔ)一個(gè)鍵值和數(shù)據(jù)的,如果 是 B 樹,可以存儲(chǔ)更多的節(jié)點(diǎn)數(shù)據(jù),樹的高度也會(huì)降低,因此讀取磁盤的次數(shù) 就降下來啦,查詢效率就快啦。
那為什么不是 B 樹而是 B+樹呢?
- B+樹非葉子節(jié)點(diǎn)上是不存儲(chǔ)數(shù)據(jù)的,僅存儲(chǔ)鍵值,而 B 樹節(jié)點(diǎn)中不僅存儲(chǔ) 鍵值,也會(huì)存儲(chǔ)數(shù)據(jù)。innodb 中頁的默認(rèn)大小是 16KB,如果不存儲(chǔ)數(shù)據(jù),那 么就會(huì)存儲(chǔ)更多的鍵值,相應(yīng)的樹的階數(shù)(節(jié)點(diǎn)的子節(jié)點(diǎn)樹)就會(huì)更大,樹就 會(huì)更矮更胖,如此一來我們查找數(shù)據(jù)進(jìn)行磁盤的 IO 次數(shù)有會(huì)再次減少,數(shù)據(jù)查 詢的效率也會(huì)更快。
- B+樹索引的所有數(shù)據(jù)均存儲(chǔ)在葉子節(jié)點(diǎn),而且數(shù)據(jù)是按照順序排列的,鏈 表連著的。那么 B+樹使得范圍查找,排序查找,分組查找以及去重查找變得 異常簡單。
6. 一次B+樹索引樹查找過程
假設(shè)有以下表結(jié)構(gòu),并且初始化了這幾條數(shù)據(jù)
執(zhí)行這條查詢SQL,需要執(zhí)行幾次的樹搜索操作?可以畫下對(duì)應(yīng)的索引樹結(jié)構(gòu)圖~
其實(shí)這個(gè),這個(gè)大家可以先畫出idx_age普通索引的索引結(jié)構(gòu)圖,大概如下:
再畫出id主鍵索引,我們先畫出聚族索引結(jié)構(gòu)圖,如下:
這條 SQL 查詢語句執(zhí)行大概流程是這樣的:
- 搜索idx_age 索引樹,將磁盤塊1加載到內(nèi)存,由于32<43,搜索左路分支,到磁盤尋址磁盤塊2。
- 將磁盤塊2加載到內(nèi)存中,由于32<36,搜索左路分支,到磁盤尋址磁盤塊4。
- 將磁盤塊4加載到內(nèi)存中,在內(nèi)存繼續(xù)遍歷,找到age=32的記錄,取得id = 400.
- 拿到id=400后,回到id主鍵索引樹。
- 搜索id主鍵索引樹,將磁盤塊1加載到內(nèi)存,因?yàn)?00<400<500,所以在選擇中間分支,到磁盤尋址磁盤塊3。
- 雖然在磁盤塊3,找到了id=400,但是它不是葉子節(jié)點(diǎn),所以會(huì)繼續(xù)往下找。到磁盤尋址磁盤塊8。
- 將磁盤塊8加載內(nèi)存,在內(nèi)存遍歷,找到id=400的記錄,拿到R4這一行的數(shù)據(jù),好的,大功告成。
7. 什么是回表?如何減少回表?
當(dāng)查詢的數(shù)據(jù)在索引樹中,找不到的時(shí)候,需要回到主鍵索引樹中去獲取,這個(gè)過程叫做回表。
比如在第6小節(jié)中,使用的查詢SQL
需要查詢所有列的數(shù)據(jù),idx_age?普通索引不能滿足,需要拿到主鍵id的值后,再回到id主鍵索引查找獲取,這個(gè)過程就是回表。
8. 什么是覆蓋索引?
如果我們查詢SQL的select *? 修改為 select id, age?的話,其實(shí)是不需要回表的。因?yàn)閕d和age?的值,都在idx_age索引樹的葉子節(jié)點(diǎn)上,這就涉及到覆蓋索引的知識(shí)點(diǎn)了。
覆蓋索引是select的數(shù)據(jù)列只用從索引中就能夠取得,不必回表,換句話說,查詢列要被所建的索引覆蓋。
9. 聊聊索引的最左前綴原則
索引的最左前綴原則,可以是聯(lián)合索引的最左N個(gè)字段。比如你建立一個(gè)組合索引(a,b,c)?,其實(shí)可以相當(dāng)于建了(a),(a,b),(a,b,c)三個(gè)索引,大大提高了索引復(fù)用能力。
當(dāng)然,最左前綴也可以是字符串索引的最左M個(gè)字符。。比如,你的普通索引樹是醬紫:
這個(gè)SQL: select * from employee where name like '小%' order by age desc; 也是命中索引的。
10. 索引下推了解過嗎?什么是索引下推
給你這個(gè)SQL:
其中,name和age?為聯(lián)合索引(idx_name_age)。
如果是Mysql5.6之前,在idx_name_age?索引樹,找出所有名字第一個(gè)字是“小”?的人,拿到它們的主鍵id,然后回表找出數(shù)據(jù)行,再去對(duì)比年齡和性別等其他字段。如圖:
有些朋友可能覺得奇怪,idx_name_age(name,age)?不是聯(lián)合索引嘛?為什么選出包含“小”?字后,不再順便看下年齡age?再回表呢,不是更高效嘛?所以呀,MySQL 5.6就引入了索引下推優(yōu)化,可以在索引遍歷過程中,對(duì)索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數(shù)。
因此,MySQL5.6版本之后,選出包含“小”?字后,順表過濾age=28
11. 大表如何添加索引
如果一張表數(shù)據(jù)量級(jí)是千萬級(jí)別以上的,那么,如何給這張表添加索引?
我們需要知道一點(diǎn),給表添加索引的時(shí)候,是會(huì)對(duì)表加鎖的。如果不謹(jǐn)慎操作,有可能出現(xiàn)生產(chǎn)事故的。可以參考以下方法:
- 先創(chuàng)建一張跟原表A數(shù)據(jù)結(jié)構(gòu)相同的新表B。
- 在新表B添加需要加上的新索引。
- 把原表A數(shù)據(jù)導(dǎo)到新表B
- rename?新表B為原表的表名A,原表A換別的表名;
12. 如何知道語句是否走索引查詢?
explain查看SQL的執(zhí)行計(jì)劃,這樣就知道是否命中索引了。
當(dāng)explain與SQL一起使用時(shí),MySQL將顯示來自優(yōu)化器的有關(guān)語句執(zhí)行計(jì)劃的信息。
一般來說,我們需要重點(diǎn)關(guān)注type、rows、filtered、extra、key。
1.2.1 type
type表示連接類型,查看索引執(zhí)行情況的一個(gè)重要指標(biāo)。以下性能從好到壞依次:system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- system:這種類型要求數(shù)據(jù)庫表中只有一條數(shù)據(jù),是const類型的一個(gè)特例,一般情況下是不會(huì)出現(xiàn)的。
- const:通過一次索引就能找到數(shù)據(jù),一般用于主鍵或唯一索引作為條件,這類掃描效率極高,,速度非???。
- eq_ref:常用于主鍵或唯一索引掃描,一般指使用主鍵的關(guān)聯(lián)查詢
- ref : 常用于非主鍵和唯一索引掃描。
- ref_or_null:這種連接類型類似于ref,區(qū)別在于MySQL會(huì)額外搜索包含NULL值的行
- index_merge:使用了索引合并優(yōu)化方法,查詢使用了兩個(gè)以上的索引。
- unique_subquery:類似于eq_ref,條件用了in子查詢
- index_subquery:區(qū)別于unique_subquery,用于非唯一索引,可以返回重復(fù)值。
- range:常用于范圍查詢,比如:between ... and 或 In 等操作
- index:全索引掃描
- ALL:全表掃描
1.2.2 rows
該列表示MySQL估算要找到我們所需的記錄,需要讀取的行數(shù)。對(duì)于InnoDB表,此數(shù)字是估計(jì)值,并非一定是個(gè)準(zhǔn)確值。
1.2.3 filtered
該列是一個(gè)百分比的值,表里符合條件的記錄數(shù)的百分比。簡單點(diǎn)說,這個(gè)字段表示存儲(chǔ)引擎返回的數(shù)據(jù)在經(jīng)過過濾后,剩下滿足條件的記錄數(shù)量的比例。
1.2.4 extra
該字段包含有關(guān)MySQL如何解析查詢的其他信息,它一般會(huì)出現(xiàn)這幾個(gè)值:
- Using filesort:表示按文件排序,一般是在指定的排序和索引排序不一致的情況才會(huì)出現(xiàn)。一般見于order by語句
- Using index :表示是否用了覆蓋索引。
- Using temporary: 表示是否使用了臨時(shí)表,性能特別差,需要重點(diǎn)優(yōu)化。一般多見于group by語句,或者union語句。
- Using where : 表示使用了where條件過濾.
- Using index condition:MySQL5.6之后新增的索引下推。在存儲(chǔ)引擎層進(jìn)行數(shù)據(jù)過濾,而不是在服務(wù)層過濾,利用索引現(xiàn)有的數(shù)據(jù)減少回表的數(shù)據(jù)。
1.2.5 key
該列表示實(shí)際用到的索引。一般配合possible_keys列一起看。
13.Hash 索引和 B+樹區(qū)別是什么?你在設(shè)計(jì)索引是怎么抉擇的?
- B+樹可以進(jìn)行范圍查詢,Hash 索引不能。
- B+樹支持聯(lián)合索引的最左側(cè)原則,Hash 索引不支持。
- B+樹支持 order by 排序,Hash 索引不支持。
- Hash 索引在等值查詢上比 B+樹效率更高。(但是索引列的重復(fù)值很多的話,Hash沖突,效率降低)。
- B+樹使用 like 進(jìn)行模糊查詢的時(shí)候,like 后面(比如%開頭)的話可以起到優(yōu)化的作用,Hash 索引根本無法進(jìn)行模糊查詢。
14. 索引有哪些優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
- 索引可以加快數(shù)據(jù)查詢速度,減少查詢時(shí)間
- 唯一索引可以保證數(shù)據(jù)庫表中每一行的數(shù)據(jù)的唯一性
缺點(diǎn):
- 創(chuàng)建索引和維護(hù)索引要耗費(fèi)時(shí)間
- 索引需要占物理空間,除了數(shù)據(jù)表占用數(shù)據(jù)空間之外,每一個(gè)索引還要占用一定的物理空間
- 以表中的數(shù)據(jù)進(jìn)行增、刪、改的時(shí)候,索引也要?jiǎng)討B(tài)的維護(hù)。
15. 聚簇索引與非聚簇索引的區(qū)別
聚簇索引并不是一種單獨(dú)的索引類型,而是一種數(shù)據(jù)存儲(chǔ)方式。它表示索引結(jié)構(gòu)和數(shù)據(jù)一起存放的索引。非聚集索引是索引結(jié)構(gòu)和數(shù)據(jù)分開存放的索引。
接下來,我們分不同存存儲(chǔ)引擎去聊哈~
在MySQL的InnoDB存儲(chǔ)引擎中, 聚簇索引與非聚簇索引最大的區(qū)別,在于葉節(jié)點(diǎn)是否存放一整行記錄。聚簇索引葉子節(jié)點(diǎn)存儲(chǔ)了一整行記錄,而非聚簇索引葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵信息,因此,一般非聚簇索引還需要回表查詢。
- 一個(gè)表中只能擁有一個(gè)聚集索引(因?yàn)橐话憔鄞厮饕褪侵麈I索引),而非聚集索引一個(gè)表則可以存在多個(gè)。
- 一般來說,相對(duì)于非聚簇索引,聚簇索引查詢效率更高,因?yàn)椴挥没乇怼?/li>
而在MyISM存儲(chǔ)引擎中,它的主鍵索引,普通索引都是非聚簇索引,因?yàn)閿?shù)據(jù)和索引是分開的,葉子節(jié)點(diǎn)都使用一個(gè)地址指向真正的表數(shù)據(jù)。