mysql order by是怎麼工做的?

假設咱們要查詢一個市民表中城市=杭州的全部人的名字,而且按照名字排序mysql

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語句能夠這樣寫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上面創建了索引spa

咱們先看下該索引結構設計

從圖中能夠看出知足city=杭州的條件是ID_X到ID_Y之間的數據3d

一般狀況下這個語句的執行流程以下:code

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

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=杭州循環結束

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

 

固然這裏絕對不是說遇到問題就加索引,這裏只是舉個例子,由於畢竟維護索引也是有代價的

瞭解更多:https://www.toutiao.com/c/user/83293539887/#mid=1633933053814798

相關文章
相關標籤/搜索