order by
在 Mysql 底層是如何執行的嗎?
蘇州
的全部人名字,而且按照姓名進行排序返回前 1000 我的的姓名、年齡,這條 sql 語句應該如何寫?
CREATE TABLE user (
id int(11) NOT NULL,
city varchar(16) NOT NULL,
name varchar(16) NOT NULL,
age int(11) NOT NULL,
PRIMARY KEY (id),
KEY city (city)
) ENGINE=InnoDB;
複製代碼
select city,name,age from user where city='蘇州' order by name limit 1000;
複製代碼
city
這個字段上添加了索引,固然城市的字段很小,不用考慮字符串的索引問題,以前有寫過一篇關於如何給字符串的加索引的文章,有不瞭解朋友看一下這篇文章:
Mysql 性能優化:如何給字符串加索引?
Explain
來分析一下的這條查詢語句的執行狀況,結果以下圖:
Extra
這個字段中的
Using filesort
表示的就是須要排序,MySQL 會給每一個線程分配一塊內存用於排序,稱爲
sort_buffer
。
city
這棵索引樹的結構,以下圖:
city='蘇州'
是從
ID3
到
IDX
這些記錄。
city='蘇州'
條件的
主鍵id
,也就是圖中的
ID3
。
主鍵id索引
取出整行,取
name
、
city
、
age
三個字段的值,存入
sort_buffer
中。
city
取下一個記錄的主鍵 id。
IDX
。
sort_buffer
中的數據按照字段
name
作快速排序。
全字段排序
,執行的流程圖以下:
按name排序
這個動做,可能在內存中完成,也可能須要使用外部排序,這取決於排序所需的內存和參數
sort_buffer_size
。
sort_buffer_size
:就是 MySQL 爲排序開闢的內存(sort_buffer)的大小。若是要排序的數據量小於 sort_buffer_size,排序就在內存中完成。但若是排序數據量太大,內存放不下,則不得不利用
磁盤臨時文件
輔助排序。
sort_buffer
和
臨時文件
中執行的。
但這個算法有一個問題,就是若是查詢要返回的字段不少的話,那麼sort_buffer
裏面要放的字段數太多,這樣內存裏可以同時放下的行數不多,要分紅不少個臨時文件,排序的性能會不好。
max_length_for_sort_data
這個參數使其使用另一種算法。max_length_for_sort_data,是 MySQL 中專門控制用於排序的行數據的長度的一個參數。它的意思是,若是單行的長度超過這個值,MySQL 就認爲單行太大,要換一個算法。
city
、
name
、
age
這三個字段的定義總長度是
36
,我把
max_length_for_sort_data
設置爲 16,咱們再來看看計算過程有什麼改變。設置的 sql 語句以下:
SET max_length_for_sort_data = 16;
複製代碼
新的算法放入 sort_buffer 的字段,只有要排序的列(即 name 字段)和主鍵 id。算法
但這時,排序的結果就由於少了 city 和 age 字段的值,不能直接返回了,整個執行流程就變成以下所示的樣子:sql
sort_buffer
,肯定放入兩個字段,即
name
和
id
。
city='蘇州'
條件的
主鍵id
,也就是圖中的
ID3
。
主鍵id索引
取出整行,取 name、id 這兩個字段,存入 sort_buffer 中。
city
取下一個記錄的主鍵 id。
IDX
。
sort_buffer
中的數據按照字段
name
作快速排序。
這個執行流程的示意圖以下,我把它稱爲rowid排序
。 性能優化
對比全字段排序
,rowid排序
多了一次回表查詢
,便是多了第7步
的查詢主鍵索引樹。bash
order by
語句,都須要排序操做的。從上面分析的執行過程,咱們能夠看到,MySQL 之因此須要生成臨時表,而且在臨時表上作排序操做,其緣由是
原來的數據都是無序的。
city
這個索引上取出來的行,自然就是按照 name 遞增排序的話,是否是就能夠不用再排序了呢?
(city,name)
聯合索引,sql 語句以下:
alter table user add index city_user(city, name);
複製代碼
city='蘇州'
的記錄,而且額外確保了,接下來按順序取「下一條記錄」的遍歷過程當中,只要 city 的值是杭州,name 的值就必定是有序的。
Extra
字段中沒有
Using filesort
了,也就是不須要排序了。並且因爲
(city,name)
這個聯合索引自己有序,
因此這個查詢也不用把 4000 行全都讀一遍,只要找到知足條件的前 1000 條記錄就能夠退出了。也就是說,在咱們這個例子裏,只須要掃描 1000 次。
(city,name,age)
聯合索引,這樣在執行上面的查詢語句就能使用覆蓋索引了,避免了回表查詢了,sql 語句以下:
alter table user add index city_user_age(city, name, age);
複製代碼
order by
語句的幾種算法流程。