前沿
學習了mysql的next-key-lock后,現在正式進入sort by的學習階段。有時在項目里會用到sort by語句。我也聽說sort by有可能會對mysql造成壓力,所以要學習一下sort by的過程,從而才能深刻的了解自己的sort by語句對mysql的壓力,避免線上性能低下或者事故。
場景
有這么一個場景,要對杭州市民按照姓名排序,取前1000個人。
給city加索引,表設計如下:
1 CREATE TABLE `t` ( `id` int(11) NOT NULL, 2 `city` varchar(16) NOT NULL, 3 `name` varchar(16) NOT NULL, 4 `age` int(11) NOT NULL, 5 `addr` varchar(128) DEFAULT NULL, 6 PRIMARY KEY (`id`), 7 KEY `city` (`city`)) ENGINE=InnoDB;
然后執行:
1 select city,name,age from t where city='杭州' order by name limit 1000 ;
全字段排序
那接下來引擎層是如何執行的呢?大致過程如下:
1、初始化該sql線程的sort buffer,其大小是sort buffer size決定的。
2、city索引上查找第一個city='杭州'的記錄,取出id
3、到主鍵索引取出select所需的三個字段:city、name和age,插入到sort buffer中;
4、然后繼續在city索引上查找下一個記錄,重復2、3步,直到city不等於'杭州';
5、在sort buffer中按照name進行排序
6、取出前1000的記錄,返回給用戶
這邊有一個問題,就是如果數據量太大,那么sort buffer不一定能放下。那此時就要借助磁盤輔助排序。mysql在這邊做了優化,是將數據放到若干個小的臨時文件中,先各自排序,然后合並成一個大的臨時文件,應該是類似於歸並排序吧。
上述排序的好處是讀完原表中數據后只需借助sort buffer和臨時文件排序就行了。
row id排序
還有一個問題,就是要返回的當行數據過大時,會導致sort buffer中放不了多少行數據,就不得不借助臨時文件排序,影響性能。row id排序可以緩解這種情況。通過如下配置,告知mysql單行超過多大需要使用row id排序。
假設city, name, age三個字段有32字節,而我們設置超過16個字節就采用row id排序。
1 SET max_length_for_sort_data = 16;
row id排序過程大致如下:
1、初始化sort buffer,確定放入兩個字段:name和id;
2、從索引 city 找到第一個滿足 city='杭州’條件的主鍵 id
3、用主鍵id在主鍵索引中查找name字段
4、將id和name放入sort buffer中
5、索引中繼續查找下一個滿足條件的主鍵id,重復3、4步驟,直到遇到第一個不滿足條件的記錄。
6、在sort buffer中按name排序,取前1000個
7、回表,獲取這1000個id對應的city和age,返回給用戶。
這樣做的好處是,同樣大小的sort buffer可以放更多的記錄,盡可能的在內存中完成操作,代價是會多一次回表取其余字段。
借助聯合索引
如果選出來的結果是天然有序的,那么我們就不用再做額外排序了。因為索引是天然有序的,所以可以創建city和name的聯合索引,這樣選出來的結果自然是按照name排序的了
alter table t add index city_user(city, name); select name, age, city from t where city='杭州' limit 1000;
具體的執行過程就不分析了。如果想避免一次回表去讀取age字段,也可以講索引改成如下,這就是覆蓋索引。
1 alter table t add index city_user(city, name, age);