閱讀192 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[MySQL 優化] --order by 原理

一、簡介:

大部分查詢中都要使用到order by。那麼處理排序的方法有兩種:一種是使用索引,另外一種則是查詢時候使用Filesort處理。

****1. 利用索引進行排: ****
利用有序索引進行排序,當 Query ORDER BY 條件和 Query 的執行計劃中所利用的 Index 的索引鍵完全一致,且索引訪問方式為 rang、 ref 或者 index 的時候,MySQL 可以利用索引順序而直接取得已經排好序的數據。因為 MySQL 不需要進行實際的排序操作
2. 利用內存或者磁盤排序算法:
1. single pass
1. two pass

優缺點對比

優點 缺點
使用索引 執行insert,update,delete查詢時,索引已經排序好,隻需要依次讀取即可,處理速度快
使用Filesort 1.因不必創建索引,故無使用索引時那麼多的缺點 2.若要排序的記錄不多,在內存中進行Filesort處理,速度也非常快

不能使用索引進行排序的場景:###

  1. 排序的基準太多,無法依據某個基準創建索引
  2. 要對group by 的結果或者DISTINCT的結果進行排序時
  3. 對臨時表的結果(union union all[5.7進行優化沒有臨時表的出現])重新排序時

二、 舉例:

表結構:

5.7@3306>[employees]>show create table employees\G
*************************** 1. row ***************************
       Table: employees
Create Table: CREATE TABLE `employees` (
  `emp_no` int(11) NOT NULL,
  `birth_date` date NOT NULL,
  `first_name` varchar(14) NOT NULL,
  `last_name` varchar(16) NOT NULL,
  `gender` enum('M','F') NOT NULL,
  `hire_date` date NOT NULL,
  PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

1.使用索引進行排序:

5.7@3306>[employees]>desc select emp_no,first_name, last_name from employees order by emp_no desc limit 0,10;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | index | NULL          | PRIMARY | 4       | NULL |   10 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------+

2.使用single pass:

5.7@3306>[employees]>show status like '%sort%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Sort_merge_passes | 0     |
| Sort_range        | 0     |
| Sort_rows         | 0     |
| Sort_scan         | 0     |
+-------------------+-------+
4 rows in set (0.00 sec)

5.7@3306>[employees]>select emp_no,first_name, last_name from employees order by first_name limit 100;
分析:將select的列,order 列放入到排序緩衝,進行排序處理,排序完成後,直接將排序緩衝中的內容返回。

5.7@3306>[employees]>show status like '%sort%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Sort_merge_passes | 0     |
| Sort_range        | 0     |
| Sort_rows         | 100   |
| Sort_scan         | 1     |
+-------------------+-------+
4 rows in set (0.00 sec)

5.7@3306>[employees]>desc  select emp_no,first_name, last_name from employees order by first_name limit 100;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299423 |   100.00 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

那麼這樣會造成什麼問題:IO,網絡等

3.Two pass

5.7@3306>[employees]>show variables like "%max_length%";
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| max_length_for_sort_data | 1024  |
+--------------------------+-------+
1 row in set (0.00 sec)
排序的值需要大於上麵的值

5.7@3306>[employees]>show status like '%sort%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Sort_merge_passes | 0     |
| Sort_range        | 0     |
| Sort_rows         | 0     |
| Sort_scan         | 0     |
+-------------------+-------+
4 rows in set (0.00 sec) 

5.7@3306>[employees]> select count(1) from ( select * from employees order by first_name desc limit 100000000 ) a;  
+----------+
| count(1) |
+----------+
|   300024 |
+----------+
1 row in set (0.87 sec)

5.7@3306>[employees]>show status like '%sort%';
+-------------------+--------+
| Variable_name     | Value  |
+-------------------+--------+
| Sort_merge_passes | 25     |
| Sort_range        | 0      |
| Sort_rows         | 300024 |
| Sort_scan         | 1      |
+-------------------+--------+
4 rows in set (0.00 sec)
解釋:Sort_merge_passes  超出sort buffer的值,將數據寫入到了tmp file中

三、 優化filesort:

優先選擇第一種using index 的排序方式,在第一種方式無法滿足的情況下,盡可能讓 MySQL 選擇使用第二種單路算法來進行排序。這樣可以減少大量的隨機IO操作,很大幅度地提高排序工作的效率。
1、去掉不必要的字段
2、加大max_length_for_sort_data 參數的設置
3、增大sort buffer的設置

最後更新:2017-06-19 22:32:07

  上一篇:go  Linux CentOs7.3 Apache2.4.6 + Mysql5.7.18 + PHP5.6.3 + Laravel5.2
  下一篇:go  理解Python並發編程-PoolExecutor篇