mysql order by是怎麼工作的?

2022-04-11 09:38:15 字數 3317 閱讀 1479

假設我們要查詢乙個市民表中城市=杭州的所有人的名字,並且按照名字排序

create

table

`t` (

`id`

int(11) not

null

, `city`

varchar(16) not

null

, `name`

varchar(16) not

null

, `age`

int(11) not

null

, `addr`

varchar(128) default

null,

primary

key(`id`),

key`city` (`city`)

) engine

=innodb;

那麼sql語句可以這樣寫

select city,name,age from t where city='杭州

'order

by name limit 1000 ;

接下來我們看下explain的結果

圖中的extra這一列下面的using filesort表示需要排序,mysql會為每個連線分配一塊記憶體用於排序,就是sort_buffer,sort_buffer_size可以調整該排序記憶體大小

因為我們where條件用到了city,所以我們在city上面建立了索引

我們先看下該索引結構

從圖中可以看出滿足city=杭州的條件是id_x到id_y之間的資料

通常情況下這個語句的執行流程如下:

1.初始化sort_buffer,確定放入name,age,city三個字段

2.從索引city中找到第乙個符合條件的資料,也就是id_x這個

3.取出索引中id的值,回表查詢name,age,city的資料放入sort_buffer中

4.從索引city取下乙個符合條件的id

5.重複步驟3,4直到city的值不滿足city=杭州的條件,也就是圖中id_y

6.對sort_buffer中的資料按照name排序

7.按照排序結果取前1000行資料返回給客戶端

我們把這個排序過程叫全欄位排序

如下圖所示

上圖按name排序這個動作可能在記憶體中完成也可能需要外部排序,這取決於排序需要的記憶體大小和sort_buffer_size這個引數

如果排序需要的記憶體大於sort_buffer_size設定的數值,那麼就需要使用磁碟臨時檔案輔助排序

rowid排序

在上面的那個全欄位排序中,只對原表查詢了一次,但是如果查詢的字段很多的話,那麼sort_buffer中就會很多資料,就會使用到

磁碟臨時輔助檔案排序,這樣效能會變差。

那麼如果mysql認為單行資料過大會怎麼辦呢?

接下來設定一下這個引數為16

max_length_for_sort_data這個引數是mysql專門用來控制用於排序的行資料的單行的長度的乙個引數,如果單行資料的字段的長度超過這個引數設定的值

那麼就會使用rowid排序,比如說我們這個例子中name,age,city這三個欄位的單行資料長度之和要是大於16,那麼就會使用rowid排序

排序流程:

1.初始化sort_buffer,確定放入id,name

2.取出city索引中第乙個滿足條件的索引的id值

3.到主鍵id索引裡面取出整行,取出name,id欄位放入sort_buffer

4.去下乙個符合條件的索引記錄,放入sort_buffer中

5.重複步驟3.4直到不滿足city=杭州

6.對sort_buffer中的資料按照name進行排序

7.遍歷排序結果取出前1000行的資料的id,去表中查詢出name,age,city返回給客戶端

可以看出來rowid排序比全欄位排序多了一次表查詢就是步驟7

我們來對比下這兩個排序

如果mysql覺得記憶體不夠用就會用到rowid排序,如果記憶體夠用則用全欄位排序

也就是說mysql有個設計思想,就是如果記憶體夠,就盡量用記憶體,儘量減少磁碟的訪問

看到這裡你是不是覺得mysql排序是乙個非常複雜的流程,效能會不好,那麼是不是所有的order_by語句都要排序呢?

不是的,如果需要排序的字段天然就是有序的,那麼就不需要排序,啥意思呢,比如說我們建立乙個city和name的聯合索引

alter table t add index city_user(city, name);

作為與city索引的對比,我們看看這個索引

如果建立了這個索引那麼執行流程就變成了這樣

1.查詢出第一條聯合索引中city,name裡面city=杭州的資料的id值

2.到主鍵索引裡面取出整行,取出name,age,city欄位

3.從索引city,name去下乙個記錄主鍵id

4.重複步驟2,3直到查到1000條記錄或者不符合city=杭州迴圈結束

可以看到這個過程不需要排序,也不需要用到臨時表

用explain驗證一下

那麼這個語句還有沒有優化空間呢?

有的我們建立乙個三個欄位的聯合索引

alter table t add index city_user_age(city, name, age);

那麼流程就變成了這樣

1.查詢出索引中第一條符合條件的資料,取出city,name,age作為結果集的一部分直接返回

2.從索引繼續取下乙個符合條件的資料作為結果集的一部分直接返回

3.重複步驟2直到查到1000條記錄或者不符合city=杭州迴圈結束

這裡其實就是用到了覆蓋索引,直接不用回表查詢了

當然這裡絕對不是說遇到問題就加索引,這裡只是舉個例子,因為畢竟維護索引也是有代價的

MySQL Order By索引優化

mysql order by索引優化 mysql可以直接使用索引來滿足乙個order by 子句而無需做額外的排序。儘管 order by 不是和索引的順序準確匹配,索引還是可以被用到。在一些情況下,mysql可以直接使用索引來滿足乙個 order by 或 group by 子句而無需做額外的排序...

mysql order by 排序技巧

首先我們新建表test,如下 create table test id int 11 not null auto increment,name varchar 255 default null,primary key id engine innodb auto increment 7 default...

MySQL order by工作機制

當使用order by對查詢結果進行排序時,mysql會給每個執行緒分配一塊兒記憶體sort buffer用於排序,在使用索引的情況下,整個的排序過程如下所述 1.初始化sort buffer,確定放入結果中所需的字段 2.從索引中找到第乙個滿足條件主鍵id 3.到主鍵id索引取出整行,取所需字段的...