mysql 索引匹配規則
explain 中關鍵字
- Index Key :MySQL是用來確定掃描的資料範圍,實際就是可以利用到的MySQL索引部分,體現在Key Length。
- Index Filter:MySQL用來確定哪些資料是可以用索引去過濾,在啟用ICP後,可以用上索引的部分。
- Table Filter:MySQL無法用索引過濾,回表取回行資料後,到server層進行資料過濾。
我們細細展開。
Index Key
Index Key是用來確定MySQL的一個掃描範圍,分為上邊界和下邊界。
MySQL利用=、>=、> 來確定下邊界(first key),利用最左原則,首先判斷第一個索引鍵值在where條件中是否存在,如果存在,則判斷比較符號,如果為(=,>=)中的一種,加入下邊界的界定,然後繼續判斷下一個索引鍵,如果存在且是(>),則將該鍵值加入到下邊界的界定,停止匹配下一個索引鍵;如果不存在,直接停止下邊界匹配。
exp:
idx_c1_c2_c3(c1,c2,c3)
where c1>=1 and c2>2 and c3=1
–> first key (c1,c2)
–> c1為 ‘>=’ ,加入下邊界界定,繼續匹配下一個
–> c2 為 ‘>’,加入下邊界界定,停止匹配
上邊界(last key)和下邊界(first key)類似,首先判斷是否是否是(=,<=)中的一種,如果是,加入界定,繼續下一個索引鍵值匹配,如果是(<),加入界定,停止匹配。
exp:
idx_c1_c2_c3(c1,c2,c3)
where c1<=1 and c2=2 and c3<3
–> first key (c1,c2,c3)
–> c1為 ‘<=’,加入上邊界界定,繼續匹配下一個
–> c2為 ‘=’加入上邊界界定,繼續匹配下一個
–> c3 為 ‘<‘,加入上邊界界定,停止匹配
注:這裡簡單的記憶是,如果比較符號中包含’=’號,’>=’也是包含’=’,那麼該索引鍵是可以被利用的,可以繼續匹配後面的索引鍵值;如果不存在’=’,也就是’>’,'<‘,這兩個,後面的索引鍵值就無法匹配了。同時,上下邊界是不可以混用的,哪個邊界能利用索引的的鍵值多,就是最終能夠利用索引鍵值的個數。
Index Filter
字面理解就是可以用索引去過濾。也就是欄位在索引鍵值中,但是無法用去確定Index Key的部分。
exp:
idex_c1_c2_c3
where c1>=1 and c2<=2 and c3 =1
index key –> c1
index filter–> c2 c3
這裡為什麼index filter 只是c1呢?因為c2 是用來確定上邊界的,但是上邊界的c1沒有出現(<=,=),而下邊界中,c1是>=,c2沒有出現,因此index key 只有c1欄位。c2,c3 都出現在索引中,被當做index filter。
Table Filter
無法利用索引完成過濾,就只能用table filter。此時引擎層會將行資料返回到server層,然後server層進行table filter。
四、Between 和Like 的處理
那麼如果查詢中存在between 和like,MySQL是如何進行處理的呢?
Between
where c1 between 'a' and 'b'
等價於 where c1>='a' and c1 <='b'
,所以進行相應的替換,然後帶入上層模型,確定上下邊界即可。
Like
首先需要確認的是%不能是最在最左側,where c1 like '%a'
這樣的查詢是無法利用索引的,因為索引的匹配需要符合最左字首原則。
where c1 like 'a%'
其實等價於 where c1>='a' and c1<'b'
大家可以仔細思考下。
五、索引的排序
在資料庫中,如果無法利用索引完成排序,隨著過濾資料的資料量的上升,排序的成本會越來越大,即使是採用了limit,但是資料庫是會選擇將結果集進行全部排序,再取排序後的limit 記錄,而且MySQL 針對可以用索引完成排序的limit 有優化,更能減少成本。
Make sure it uses index It is very important to have ORDER BY with LIMIT executed without scanning and sorting full result set, so it is important for it to use index – in this case index range scan will be started and query execution stopped as soon as soon as required amount of rows generated.
CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` int(11) NOT NULL DEFAULT '0', `c2` int(11) NOT NULL DEFAULT '0', `c3` int(11) NOT NULL DEFAULT '0', `c4` int(11) NOT NULL DEFAULT '0', `c5` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_c1_c2_c3` (`c1`,`c2`,`c3`)) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 select * from t1; +----+----+----+----+----+----+ | id | c1 | c2 | c3 | c4 | c5 | +----+----+----+----+----+----+ | 1 | 3 | 3 | 2 | 0 | 0 | | 2 | 2 | 4 | 5 | 0 | 0 | | 3 | 3 | 2 | 4 | 0 | 0 | | 4 | 1 | 3 | 2 | 0 | 0 | | 5 | 1 | 3 | 3 | 0 | 0 | | 6 | 2 | 3 | 5 | 0 | 0 | | 7 | 3 | 2 | 6 | 0 | 0 | +----+----+----+----+----+----+ 7 rows in set (0.00 sec) select c1,c2,c3 from t1; +----+----+----+ | c1 | c2 | c3 | +----+----+----+ | 1 | 3 | 2 | | 1 | 3 | 3 | | 2 | 3 | 5 | | 2 | 4 | 5 | | 3 | 2 | 4 | | 3 | 2 | 6 | | 3 | 3 | 2 | +----+----+----+ 7 rows in set (0.00 sec)
存在一張表,c1,c2,c3上面有索引,select c1,c2,c3 from t1
;
查詢走的是索引全掃描,因此呈現的資料相當於在沒有索引的情況下select c1,c2,c3 from t1 order by c1,c2
,c3
;
的結果。
因此,索引的有序性規則是怎麼樣的呢?
c1=3 —> c2 有序,c3 無序
c1=3,c2=2 — > c3 有序
c1 in(1,2) —> c2 無序 ,c3 無序
有個小規律,idx_c1_c2_c3,那麼如何確定某個欄位是有序的呢?c1 在索引的最前面,肯定是有序的,c2在第二個位置,只有在c1 唯一確定一個值的時候,c2才是有序的,如果c1有多個值,那麼c2 將不一定有序,同理,c3也是類似