MySQL - order by和 group by 优化初探
文章目錄
- 生猛干貨
- DB Version
- Table
- 數據量
- 案例一 :explain select * from employees where name = 'LiLei' and position = 'dev' order by age
- 案例二: explain select * from employees where name = 'LiLei' order by position
- 案例三:explain select * from employees where name = 'LiLei' order by age , position
- 案例四:explain select * from employees where name = 'LiLei' order by position , age
- 案例五:explain select * from employees where name = 'LiLei' and age = 18 order by position , age ;
- 案例六:explain select * from employees where name = 'LiLei' order by age asc , position desc ;
- 案例七:explain select * from employees where name in ('HanMeiMei' , 'LiLei') order by age , position ;
- 案例八: explain select * from employees where name > 'HanMeiMei' order by name ;
- group by 優化
- 小結
- 搞定MySQL
生猛干貨
帶你搞定MySQL實戰,輕松對應海量業務處理及高并發需求,從容應對大場面試
DB Version
mysql> select version(); +-----------+ | version() | +-----------+ | 5.7.28 | +-----------+ 1 row in setmysql>Table
CREATE TABLE `employees` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';兩個索引
重點就是這個二級索引 ,記號了哈。
數據量
mysql> select count(1) from employees ; +----------+ | count(1) | +----------+ | 100002 | +----------+ 1 row in setmysql>案例一 :explain select * from employees where name = ‘LiLei’ and position = ‘dev’ order by age
explain select * from employees where name = 'LiLei' and position = 'dev' order by age ;先想一下這個order by 會不會走索引 ?
會走索引
原因呢 ?
腦海中要有這個聯合索引在MySQL底層的B+Tree的數據結構 , 索引 排好序的數據結構。
name = ‘LiLei’ and position = ‘dev’ order by age
name 為 LiLei , name 確定的情況下, age 肯定是有序的 ,age 有序不能保證position 有序
所以 這個order by age 是可以走索引的
繼續分析下這個explain
mysql> explain select * from employees where name = 'LiLei' and position = 'dev' order by age ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 10 | Using index condition | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ 1 row in setorder by 走的索引 是不會體現在key_len上的, 這個74 = 3 * 24 + 2 , 是計算的name 。 最左匹配原則 ,中間字段不能斷,因此查詢用到了name索引。
但是 Extra直接里面可以看出來 Using index condition ,說明age索引列用在了排序過程中 。 如果沒有走索引的話,那就是 Using FileSort 了
接下來繼續看幾個例子,加深理解,重點是腦海中的 索引B+Tree結構
案例二: explain select * from employees where name = ‘LiLei’ order by position
mysql> explain select * from employees where name = 'LiLei' order by position ;想一想,這個order by 會走索引嗎?
我們來看下索引 KEY idx_name_age_position (name,age,position) USING BTREE
再來看下查詢SQL
where name = 'LiLei' order by position ;name = LiLei , name 值能確定下來, 符合最左匹配原則 所以查詢會走索引 , 用了聯合索引中的name字段, key len = 74 . 所以 Using index condition
order by position , 在索引中 中間缺失了age , 用position ,跳過了age , 那索引樹能是有序的嗎? 肯定不是。。。所以 position肯定不是排好序的 , 無法走索引排序,因此 Extra信息 有 Using filesort
來看下執行計劃
mysql> explain select * from employees where name = 'LiLei' order by position ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100 | Using index condition; Using filesort | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ 1 row in setmysql>正如分析~
有感覺了嗎? 再來看一個
案例三:explain select * from employees where name = ‘LiLei’ order by age , position
這個SQL和案例二的很相似 , 僅僅在排序的時候在前面多了一個age字段參與排序 , 那分析分析 order by 會走索引嗎
mysql> explain select * from employees where name = 'LiLei' order by age , position ;時刻不要那個索引樹 ,來分析一下
name = LiLei , name 固定,結合 建立的索引, 最左原則,所以查詢肯定會走聯合索引中的部分索引 name .
在name都是LiLei 的情況下 , order by age , position 結合索引樹 ,age和position用于排序 也是有序的,應該不會走using filesort
我們來看下執行計劃
mysql> explain select * from employees where name = 'LiLei' order by age , position ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100 | Using index condition | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+ 1 row in setmysql>案例四:explain select * from employees where name = ‘LiLei’ order by position , age
再分析一個,和案例上也很像。 把 order by的排序順序 調整一下,我們來分析一下 order by會不會走索引
explain select * from employees where name = 'LiLei' order by position , age ; mysql> explain select * from employees where name = 'LiLei' order by position , age ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100 | Using index condition; Using filesort | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ 1 row in setmysql>咦 , 執行計劃中有 using filesort
為什么呢?
看看我們二級索引的建立的字段順序 , 創建順序為name,age,position,但是排序的時候age和position顛倒位置了, 那排好序的特性肯定就無法滿足了,那你讓MySQL怎么走索引?
案例五:explain select * from employees where name = ‘LiLei’ and age = 18 order by position , age ;
這個order by 會走索引嗎?
mysql> explain select * from employees where name = 'LiLei' and age = 18 order by position , age ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 78 | const,const | 1 | 100 | Using index condition | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-----------------------+ 1 row in setmysql>走了dx_name_age_position 索引中的 name 和 age , order by 其實也走了索引,你看extra中并沒有 using filesort ,因為age為常量,在排序中被MySQL優化了,所以索引未顛倒,不會出現Using filesort
案例六:explain select * from employees where name = ‘LiLei’ order by age asc , position desc ;
mysql> explain select * from employees where name = 'LiLei' order by age asc , position desc ; +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100 | Using index condition; Using filesort | +----+-------------+-----------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+---------------------------------------+ 1 row in set我們可以看到雖然排序的字段列與建立索引的順序一樣, order by默認升序排列,而SQL中的 position desc變成了降序排列,導致與索引的排序方式不同,從而產生Using filesort。
Note: Mysql8以上版本有降序索引可以支持該種查詢方式。
案例七:explain select * from employees where name in (‘HanMeiMei’ , ‘LiLei’) order by age , position ;
mysql> explain select * from employees where name in ('HanMeiMei' , 'LiLei') order by age , position ; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 2 | 100 | Using index condition; Using filesort | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+---------------------------------------+ 1 row in setmysql>對order by 來講 ,多個相等的條件也是 范圍查詢。 既然是范圍查詢, 可能對于每個值在索引中是有序的,但多個合并在一起,就不是有序的了,所以 using filesort .
案例八: explain select * from employees where name > ‘HanMeiMei’ order by name ;
mysql> explain select * from employees where name > 'HanMeiMei' order by name ; +----+-------------+-----------+------------+------+-----------------------+------+---------+------+-------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+-------+----------+-----------------------------+ | 1 | SIMPLE | employees | NULL | ALL | idx_name_age_position | NULL | NULL | NULL | 96845 | 50 | Using where; Using filesort | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+-------+----------+-----------------------------+ 1 row in setmysql>MySQL自己內部有一套優化機制,且數據量不同、版本不一樣,結果也可能有差異
一般情況下, 聯合索引第一個字段用范圍不一定會走索引 , 可以采用 覆蓋索引進行優化,避免回表帶來的性能開銷 。
mysql> explain select namefrom employees where name > 'a' order by name ; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 48422 | 100 | Using where; Using index | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ 1 row in setmysql>group by 優化
-
group by與order by類似,其實質是先排序后分組,遵照索引創建順序的最左前綴法則。
-
對于group by的優化如果不需要排序的可以加上order by null禁止排序。
-
where高于having,能寫在where中的限定條件就不要去having限定了。
小結
-
MySQL支持兩種方式的排序filesort和index,Using index是指MySQL掃描索引本身完成排序
-
order by滿足兩種情況會使用Using index
A: order by語句使用索引最左前列。
B: 使用where子句與order by子句條件列組合滿足索引最左前列 -
盡量在索引列上完成排序,遵循索引建立(索引創建的順序)時的最左前綴法則
-
如果order by的條件不在索引列上,就會產生Using filesort
-
能用覆蓋索引盡量用覆蓋索引
搞定MySQL
總結
以上是生活随笔為你收集整理的MySQL - order by和 group by 优化初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL - 使用trace工具来窥
- 下一篇: MySQL - 分页查询优化的两个案例