---
title: 不懂SQL優化?那你就OUT了(六)
MySQL如何優化--ORDER BY
date: 2018-12-08
categories: 數據庫優化
---
在日常開發中,我們經常會使用 order by 子句對某些數據進行排序處理,那么在mysql中使用order by子句時,我們應該怎樣優化order by 子句后的查詢字段來提高查詢效率和排序速度。
在mysql中使用 order by 進行排序有兩種方式:
1. 掃描有序索引進行排序(推薦)
2. 使用文件進行排序(using filesort:內存/磁盤文件排序獲取結果 )
在InnoDB存儲引擎以B+樹作為索引的底層實現,B+樹的葉子節點存儲着所有數據頁而內部節點不存放數據信息,並且所有葉子節點形成一個(雙向)鏈表。
你可以簡單的理解為:索引是一種特殊的文件,當我們建立索引時,mysql會使用雙向鏈表的方式來事先給數據進行排序。
例如:
CREATE TABLE t_testOrderBy(
userid INT PRIMARY KEY AUTO_INCREMENT, -- 用戶編號
username VARCHAR(25), -- 用戶姓名
userAge INT, -- 用戶年齡
usergender CHAR(3), -- 用戶性別
provice VARCHAR(25), -- 所在省份
city VARCHAR(25), -- 所在城市
address VARCHAR(200) -- 詳細地址
);
測試數據:
INSERT INTO t_testOrderBy VALUES(NULL,'張三',18,'男','四川省','成都市','xxxx路222號');
INSERT INTO t_testOrderBy VALUES(NULL,'李四',20,'女','雲南省','昆明市','xxx北路12號');
INSERT INTO t_testOrderBy VALUES(NULL,'王五',24,'男','貴州省','遵義市','xxxxx路18號');
INSERT INTO t_testOrderBy VALUES(NULL,'趙六',19,'女','四川省','綿陽市','xx路234號');
INSERT INTO t_testOrderBy VALUES(NULL,'孫琦',28,'男','雲南省','玉溪市','xxxx路324號');
INSERT INTO t_testOrderBy VALUES(NULL,'王曉琪',21,'女','雲南省','玉溪市','xxxx路123號');
例如:
圖1:
從圖1可以看出:
使用userid進行排序時,userid上有主鍵索引,mysql會直接遍歷userid索引的葉子節點鏈表,不需要進行額外的排序操作,這就是用索引掃描來排序。
使用 username 字段上沒有任何索引,此時B+樹結構不存在,mysql就只能先掃表篩選出符合條件的數據,再將篩選結果根據username排序。這個排序過程就是filesort。
### 使用有序索引排序時
sql語句中,where子句和order by 子句都可以使用索引, where子句使用索引避免全表掃描,order by 子句使用索引盡量避開使用文件排序(filesort),以提高查詢效率。
雖然索引能提高查詢效率,但在一條sql里,對於一張表的查詢 一次只能使用一個索引(注:排除發生index merge的可能性),也就是說當where子句與order by 子句使用的索引不一致時,MySQL只能使用其中一個索引(B+樹)。
####order by 可以使用索引
1. 當select的字段包含在索引中時,能利用到索引排序功能,進行覆蓋索引掃描.
例如: 為表中的username添加索引。
可以看出當 select 字段中包含了 userage(未建立索引)時,則不能使用索引。
2.當有聯合索引時,order by 子句使用索引必須遵循索引的最左前綴原則。
例如: 為 省份,城市,詳細地址建立聯合索引
當不遵循最左前綴原則時,則會使用filesort
3.聯合索引中的一部分做等值查詢 ,另一部分作為排序字段。(當然還是要遵循最左前綴原則)
####order by 不使用索引排序
1.select字段在多個索引中,無法使用索引排序。
從上圖中可以看出,username的一個獨立的所以,二而provice是聯合索引,當select字段中有多個索引列時,無法使用索引排序
2.對不同的關鍵字使用ORDER BY:
3.當有聯合索引時,order by 子句使用索引 不 遵循索引的最左前綴原則,無法使用索引排序
4. 升降序不一致,無法使用索引排序。
5.order by 的字段使用函數
6.返回數據量過大也會不使用索引。
## 使用文件進行排序(后面在介紹)
對於不能利用索引避免排序的sql,數據庫不得不自己實現排序功能以滿足用戶需求,此時sql的執行計划中會出現“Using filesort”,這里需要注意的是filesort並不意味着就是文件排序,其實也有可能是內存排序,這個主要由sort_buffer_size參數與結果集大小確定。
如果排序不可避免,可以用下面的辦法加速:
1. 避免使用 “select * ” 。
2. 增加sort_buffer_size變量的大小。
3. 增加read_rnd_buffer_size變量的大小。
4. 更改tmpdir指向具有大量空閑空間的專用文件系統。
5. 使用合適的列大小存儲具體的內容,比如對於city字段 varchar(25)比varchar(200)能獲取更好的性能.
##order by 總結