-- 示例表
CREATE TABLE `employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(20) NOT NULL DEFAULT '0' COMMENT '年齡',
`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',
`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '入職時間',
PRIMARY KEY (`id`),
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE,
KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=136326 DEFAULT CHARSET=utf8 COMMENT='員工表'
Order by與Group by優化
EXPLAIN select * from employees WHERE name='LiLei' and position='dev' order by age;
利用最左前綴法則:中間字段不能斷,因此查詢用到了name索引,從key_len=74也能看出,age索引列用在排序的過程中,因為Extra字段里沒有using filesort。
EXPLAIN select * from employees WHERE name='LiLei' order by position;
從explain的執行結果來看:key_len=74, 查詢使用name索引,由於用了position進行排序,跳過了age,出現了Using filesort。
EXPLAIN select * from employees WHERE name='LiLei' order by age,position;
查找只用到了name索引,age和position用於排序,無Using filesort。
EXPLAIN select * from employees WHERE name='LiLei' order by position,age;
和上一個case不同的是,Extra中出現了Using filesort,因為索引的創建順序為name,age,position,但是排序的時候age和position顛倒了位置。
EXPLAIN select * from employees WHERE name='LiLei' order by age asc, position desc;
雖然排序的字段和聯合索引順序是一樣的,且order by是默認升序,這里position desc是降序,導致與索引的排序方式不同,從而產生Using filesort。Mysql8以上版本有降序索引可以支持該種查詢方式。
EXPLAIN select * from employees WHERE name in('LiLei', 'zhuge') order by age, position ;
對於排序來說,多個相等條件也是范圍查詢。
EXPLAIN select * from employees WHERE name > 'a' order by name;
可以用覆蓋索引優化
EXPLAIN select name,age,position from employees WHERE name > 'a' order by name;
filesort排序
EXPLAIN select * from employees where name='LiLei' order by position;
查看這條sql對應trace結果(只展示排序部分):
set session optimizer_trace="enabled=on",end_markers_in_json=on; ‐‐開啟trace
select * from employees where name = 'LiLei' order by position;
select * from information_schema.OPTIMIZER_TRACE;
{
"join_execution": { --sql執行階段
"select#": 1,
"steps": [
{
"filesort_information": [
{
"direction": "asc",
"table": "`employees`",
"field": "position"
}
] /* filesort_information */,
"filesort_priority_queue_optimization": {
"usable": false,
"cause": "not applicable (no LIMIT)"
} /* filesort_priority_queue_optimization */,
"filesort_execution": [
] /* filesort_execution */,
"filesort_summary": { --文件排序信息
"rows": 1, --預計掃描行數
"examined_rows": 1, --參與排序的行
"number_of_tmp_files": 0, --使用臨時文件的個數,這個值為0代表全部使用sort_buffer內存排序,否則使用磁盤文件排序
"sort_buffer_size": 200704, --排序緩存的大小
"sort_mode": "<sort_key, additional_fields>" --排序方式,這里用的單路排序
} /* filesort_summary */
}
] /* steps */
} /* join_execution */
}
修改max_length_for_sort_data=10
set max_length_for_sort_data = 10; --employees表所有字段長度總和肯定大於10字節
select * from employees where name = 'LiLei' order by position;
select * from information_schema.OPTIMIZER_TRACE;
{
"join_execution": {
"select#": 1,
"steps": [
{
"filesort_information": [
{
"direction": "asc",
"table": "`employees`",
"field": "position"
}
] /* filesort_information */,
"filesort_priority_queue_optimization": {
"usable": false,
"cause": "not applicable (no LIMIT)"
} /* filesort_priority_queue_optimization */,
"filesort_execution": [
] /* filesort_execution */,
"filesort_summary": {
"rows": 1,
"examined_rows": 1,
"number_of_tmp_files": 0,
"sort_buffer_size": 53248,
"sort_mode": "<sort_key, rowid>" --排序方式為雙路排序
} /* filesort_summary */
}
] /* steps */
} /* join_execution */
}
對比這兩個排序模式,單路排序會把所有的需要查詢的字段數據都放到sort_buffer中,而雙路排序只會把主鍵id和需要排序的字段放到sort_buffer中進行排序,然后再通過主鍵id 回到原表 查詢需要的字段數據。MySQL通過max_length_for_sort_data這個參數來控制排序,在不同場景下使用不同的排序模式,從而提升排序效率。
優化總結
- Mysql支持兩種方式的排序filesort和index,using index是指Mysql掃描索引本身完成排序。index效率高,filesort效率低。
- order by滿足兩種情況會使用using index。
order by語句使用索引最左前列。
使用where子句和order by子句 條件列組合滿足索引最左前列。 - 盡量在索引列上完成排序,遵循索引建立(索引創建的順序)時候的最左前綴法則。
- 如果order by 的條件不在索引列上,就會產生using filesort。
還沒關注我的公眾號?
- 掃文末二維碼關注公眾號【小強的進階之路】可領取如下:
- 學習資料: 1T視頻教程:涵蓋Javaweb前后端教學視頻、機器學習/人工智能教學視頻、Linux系統教程視頻、雅思考試視頻教程;
- 100多本書:包含C/C++、Java、Python三門編程語言的經典必看圖書、LeetCode題解大全;
- 軟件工具:幾乎包括你在編程道路上的可能會用到的大部分軟件;
- 項目源碼:20個JavaWeb項目源碼。