簡單談談MySQL的loose index scan
眾所周知,InnoDB採用IOT(index organization table)即所謂的索引組織表,而葉子節點也就存放了所有的資料,這就意味著,資料總是按照某種順序儲存的。所以問題來了,如果是這樣一個語句,執行起來應該是怎麼樣的呢?語句如下:
?
1 |
select count ( distinct a) from table1;
|
列a上有一個索引,那麼按照簡單的想法來講,如何掃描呢?很簡單,一條一條的掃描,這樣一來,其實做了一次索引全掃描,效率很差。這種掃描方式會掃描到很多很多的重複的索引,這樣說的話最佳化的辦法也是很容易想到的:跳過重複的索引就可以了。於是網上能搜到這樣的一個最佳化的辦法:
?
1 |
select count (*) from ( select distinct a from table1) t;
|
從已經搜尋到的資料看,這樣的執行計劃中的extra就從using index變成了using index for group-by。
但是,但是,但是,好在我們現在已經沒有使用5.1的版本了,大家基本上都是5.5以上了,這些現代版本,已經實現了loose index scan:
很好很好,就不需要再用這種奇技淫巧去最佳化SQL了。
文件裡關於group by這裡寫的有點意思,說是最大眾化的辦法就是進行全表掃描並且建立一個臨時表,這樣執行計劃就會難看的要命了,肯定有ALL和using temporary table了。
5.0之後group by在特定條件下可能使用到loose index scan,
?
123456 |
CREATE TABLE log_table ( id INT NOT NULL PRIMARY KEY , log_machine VARCHAR (20) NOT NULL , log_time DATETIME NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE INDEX ix_log_machine_time ON log_table (log_machine, log_time);
|
1
?
12 |
SELECT MAX (log_time) FROM log_table; SELECT MAX (log_time) FROM log_table WHERE log_machine IN ( 'Machine 1' );
|
這兩條sql都只需一次index seek便可返回,源於索引的有序排序,最佳化器意識到min/max位於最左/右塊,從而避免範圍掃描;
extra顯示Select tables optimized away ;
2
複製程式碼 程式碼如下:SELECT MAX(log_time) FROM log_table WHERE log_machine IN (‘Machine 1','Machine 2','Machine 3','Machine 4');
執行計劃type 為range(extra顯示using where; using index),即執行索引範圍掃描,先讀取所有滿足log_machine約束的記錄,然後對其遍歷找出max value;
改進
複製程式碼 程式碼如下:SELECT MAX(log_time) FROM log_table WHERE log_machine IN (‘Machine 1','Machine 2','Machine 3','Machine 4') group by log_machine order by 1 desc limit 1;
這滿足group by選擇loose index scan的要求,執行計劃的extra顯示using index for group-by,執行效果等值於
?
1234 |
SELECT MAX (log_time) FROM log_table WHERE log_machine IN (‘Machine 1 ') Union SELECT MAX(log_time) FROM log_table WHERE log_machine IN (‘Machine 2' ) …..
|
即對每個log_machine執行loose index scan,rows從原來的82636下降為16(該表總共1,000,000條記錄)。
Group by何時使用loose index scan?
適用條件:
1 針對單表操作
2 Group by使用索引的最左字首列
3 只支援聚集函式min()/max()
4 Where條件出現的列必須為=constant操作 , 沒出現在group by中的索引列必須使用constant
5 不支援字首索引,即部分列索引 ,如index(c1(10))
執行計劃的extra應該顯示using index for group-by
假定表t1有個索引idx(c1,c2,c3)
?
12345678 |
SELECT c1, c2 FROM t1 GROUP BY c1, c2; SELECT DISTINCT c1, c2 FROM t1; SELECT c1, MIN (c2) FROM t1 GROUP BY c1; SELECT c1, c2 FROM t1 WHERE c1
|
而SELECT c1, c3 FROM t1 where c3= const GROUP BY c1, c2;則可以
緊湊索引掃描tight index scan
Group by在無法使用loose index scan,還可以選擇tight,若兩者都不可選,則只能藉助臨時表;
掃描索引時,須讀取所有滿足條件的索引鍵,要麼是全索引掃描,要麼是範圍索引掃描;
Group by的索引列不連續;或者不是從最左字首開始,但是where條件裡出現最左列;
?
12 |
SELECT c1, c2, c3 FROM t1 WHERE c2 = 'a' GROUP BY c1, c3; SELECT c1, c2, c3 FROM t1 WHERE c1 = 'a' GROUP BY c2, c3;
|
5.6的改進
事實上,5.6的index condition push down可以彌補loose index scan缺失帶來的效能損失。
KEY(age,zip)
?
mysql> explain SELECT name FROM people WHERE age BETWEEN 18 AND 20 AND zip IN (12345,12346, 12347); +----+-------------+--------+-------+---------------+------+---------+------+-------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+------+---------+------+-------+-------------+ | 1 | SIMPLE | people | range | age | age | 4 | NULL | 90556 | Using where | +----+-------------+--------+-------+---------------+------+---------+------+-------+-------------+ 1 row in set (0.01 sec)
|
根據key_len=4可以推測出sql只用到索引的第一列,即先透過索引查出滿足age (18,20)的行記錄,然後從server層篩選出滿足zip約束的行;
pre-5.6,對於複合索引,只有當引導列使用"="時才有機會在索引掃描時使用到後面的索引列。
?
mysql> explain SELECT name FROM people WHERE age=18 AND zip IN (12345,12346, 12347); +----+-------------+--------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | people | range | age | age | 8 | NULL | 3 | Using where | +----+-------------+--------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
|
對比一下查詢效率
?
mysql> SELECT sql_no_cache name FROM people WHERE age=19 AND zip IN (12345,12346, 12347); +----------------------------------+ | name | +----------------------------------+ | 888ba838661aff00bbbce114a2a22423 | +----------------------------------+ 1 row in set (0.06 sec) mysql> SELECT SQL_NO_CACHE name FROM people WHERE age BETWEEN 18 AND 22 AND zip IN (12345,12346, 12347); +----------------------------------+ | name | +----------------------------------+ | ed4481336eb9adca222fd404fa15658e | | 888ba838661aff00bbbce114a2a22423 | +----------------------------------+ 2 rows in set (1 min 56.09 sec)
|
對於第二條sql,可以使用union改寫,
?
mysql> SELECT name FROM people WHERE age=18 AND zip IN (12345,12346, 12347) -> UNION ALL -> SELECT name FROM people WHERE age=19 AND zip IN (12345,12346, 12347) -> UNION ALL -> SELECT name FROM people WHERE age=20 AND zip IN (12345,12346, 12347) -> UNION ALL -> SELECT name FROM people WHERE age=21 AND zip IN (12345,12346, 12347) -> UNION ALL -> SELECT name FROM people WHERE age=22 AND zip IN (12345,12346, 12347);
|
而mysql5.6引入了index condition pushdown,從最佳化器層面解決了此類問題。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1978/viewspace-2805851/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 簡單談談Spring的IoCSpring
- 談一談對vuex的簡單理解Vue
- 簡單談談Cross Entropy LossROS
- 簡單談談DNS協議DNS協議
- 簡單談談服務間的連線
- 簡單來談談Unicode與emojiUnicode
- 談一談對vue-router的簡單理解Vue
- 簡單談談面向2024年的遊戲出海遊戲
- 簡單談談Hilt——依賴注入框架依賴注入框架
- PostgreSQL DBA(119) - pgAdmin(LIMIT:Index Scan vs Bitmap Index Scan)SQLMITIndex
- 簡單談談PCB電路板的表面處理工藝
- 談談mysql和redis的區別MySqlRedis
- “談談MySQL的基數統計”MySql
- 談談什麼是MySQL的表空間?MySql
- 談談 mysql和oracle的使用感受 -- 差異MySqlOracle
- 談談MYSQL索引是如何提高查詢效率的MySql索引
- 談談MySQL InnoDB儲存引擎事務的ACID特性MySql儲存引擎
- 面試官:談談你對mysql索引的認識?面試MySql索引
- 淺談Mysql索引MySql索引
- [20180725]index skip-scan operation.txtIndex
- 20180316不使用INDEX FULL SCAN (MIN/MAX)Index
- Oracle vs PostgreSQL Develop(31) - Index Only ScanOracleSQLdevIndex
- 談談SSO單點登入的設計實現
- [20181201]奇怪的INDEX SKIP SCAN執行計劃.txtIndex
- [20230908]Oracle Index Range Scan with LIKE Condition on Wildcard '_'.txtOracleIndex
- 細談Mysql事務MySql
- MySQL淺談(索引、鎖)MySql索引
- 談談Redis快取中MySQL的資料如何與Redis同步Redis快取MySql
- 月半談(二)基於 Redis 的限流器-簡單計數器Redis
- 談談Spring Boot 資料來源載入及其多資料來源簡單實現Spring Boot
- Java高頻面試題:談談你對MySQL索引的瞭解Java面試題MySql索引
- 淺談單調棧
- 談談最近的思考
- 【MySQL】四、Insert buffer 漫談MySql
- mysql學習方法雜談MySql
- 談談近況,談談自由職業,談談“金飯碗”
- 【TUNE_ORACLE】列出走了INDEX FULL SCAN的SQL參考OracleIndexSQL
- 【TUNE_ORACLE】列出走了INDEX SKIP SCAN的SQL參考OracleIndexSQL