點擊上方“統計與數據分析實戰”,選擇“設爲星標” ,優質文章和資源,及時送達
今天程序猿碼不停問了個問題:在MySQL的InnoDB引擎下,有表 test,表中有索引 idx_a_b_c('a', 'b', 'c') ,那麼SQL SELECT * from test WHERE c = 1 and b > 1 and a = 1
的索引命中情況是怎樣的呢?
大家一看,這不是很普通的索引最左匹配問題嗎?首先,答案是肯定的,可以命中索引!雖然索引順序是 a、b、c,但是因爲SQL執行前,MySQL查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃 SELECT * from test WHERE and a = 1 and b > 1 and c = 1 。由於 b 使用了範圍查詢,所以索引只命中了 a 和 b 兩列,b 之後的 c 列不再命中。
碼不停繼續追問:嗯…… 口說無憑據,怎麼證明呢?
EXPLAIN解析
對於SQL的執行分析,我們首先想到的就是 EXPLAIN 來解析。如果你還對 EXPLAIN 不熟悉,那該補補基礎了。這裏只做一個實戰介紹,就以上述問題中的 SQL 舉例。建表語句如下:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `index_abc` (`a`,`b`,`c`) USING BTREE,
KEY `index_ad` (`a`,`d`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC
1、執行 EXPLAIN 分析 SQL
首先,執行下面 SQL:
EXPLAIN select * from test where c = 1 and b > 1 and a = 1;
然後得到輸出:
想要分析 SQL 就需要懂得每個字段的含義
2、EXPLAIN 字段解釋
id:每個執行計劃都有一個 id,如果是一個聯合查詢,這裏還將有多個 id。
select_type:表示 SELECT 查詢類型,這條SQL 是 SIMPLE(普通查詢,即沒有聯合查詢、子查詢)類型。其他還有 PRIMARY(主查詢)、UNION(UNION 中後面的查詢)、SUBQUERY(子查詢)等。
table:當前執行計劃查詢的 test 表,如果給表起別名了,則顯示別名信息。
partitions:訪問的分區表信息。
type:表示從表中查詢到行所執行的方式,查詢方式是 SQL 優化中一個很重要的指標,結果值從好到差依次是:system > const > eq_ref > ref > range > index > ALL。
system/const:表中只有一行數據匹配,此時根據索引查詢一次就能找到對應的數據。
eq_ref:使用唯一索引掃描,常見於多表連接中使用主鍵和唯一索引作爲關聯條件。
ref:非唯一索引掃描,還可見於唯一索引最左原則匹配掃描。
range:索引範圍掃描,比如,<,>,between 等操作。
index:索引全表掃描,此時遍歷整個索引樹。
ALL:表示全表掃描,需要遍歷全表來找到對應的行。
這裏因爲使用了 b > 條件,所以使用 range 類型。
possible_keys:可能使用到的索引。因爲表中還有 index_ad 索引,所以可能用到的索引是兩個。
key:實際使用到的索引。最終判斷使用索引 index_abc.。
key_len:當前使用的索引的長度。(重要!重要!重要!重要的事情說三遍!!!)通過 key_len 便可以判斷索引的命中情況。
ref:關聯 id 等信息。
rows:查找到記錄所掃描的行數。
filtered:查找到所需記錄佔總掃描記錄數的比例。
Extra:額外信息。
通過上述結束,每個字段我們都明白什麼含義。並且證明了雖然原始 sql 中abc的順序是亂序的,但是我們還是可以命中索引 index_abc。但是具體命中索引中的哪幾列呢?通過 key_len 又如何判斷。
索引長度計算
因爲聯合索引的結構特點, 如果命中索引,那麼命中的列只有可能是這幾種情況:a、ab、abc。就像一個鏈,你無法在跳過 b 節點的情況下找到c節點。所以現在問題就轉換成,如何確認命中索引 index_abc 是命中了 a 列、ab列 還是 abc 列。想要會分析,就需要掌握索引長度的計算方法了。
1、索引長度公式
1.所有的索引字段,如果沒有設置not null,則需要加一個字節。
2.定長字段,int佔四個字節、date佔三個字節、char(n)佔n個字符。
3.對於變成字段varchar(n),則有n個字符+兩個字節。
4.不同的字符集,一個字符佔用的字節數不同。latin1編碼的,一個字符佔用一個字節,gbk編碼的,一個字符佔用兩個字節,utf8編碼的,一個字符佔用三個字節。
5.索引長度 char()、varchar()索引長度的計算公式:
Character Set:utf8mb4=4,utf8=3,gbk=2,latin1=1) * 列長度 + 1(允許null) + 2(變長列)
2、舉例:
1.SQL SELECT * from test WHERE c = 1 and b = 1 and a = 1
解析:
key_len = 4 (a是int類型 4字節)+ 1(a可爲null 1字節) + 4(b是int類型 4字節) + 1(b可爲null 1字節) + 4(c是int類型 4字節) + 1(c可爲null 1字節) = 15
2.SQL SELECT * from test WHERE b = 4 and d = c
解析:
key_len = 20(utf8每個字符 3字節) * 3 + 2(varchar) + 1(d可爲null) + 4 (a是int類型 4字節)+ 1(a可爲null 1字節) = 68
3、確定命中列
explain 的結果 key_len 長度是 10,怎麼得出來的呢?套用公式。
4 (a是int類型 4字節)+ 1(a可爲null 1字節) + 4(b是int類型 4字節) + 1(b可爲null 1字節) = 10
所以,我們得出最終結論,SQL SELECT * from test WHERE c = 1 and b > 1 and a = 1
命中了索引 index_abc 的 a和b列!
看到這裏,碼不停同學點了點頭。
◆ ◆ ◆ ◆ ◆
關注並後臺回覆 “面試” 或者 “資料”,
即可免費獲取最新2019BAT
大廠面試題和大數據視頻
如果文章有幫助到你,請點擊再看或者轉發支持!
送書啦~
規則:公衆號後臺會自動統計分享榜排名,第一名即可包郵獲得此書。
更多書籍請戳:優質圖書