mysql?order?by?排序原理解析
sql語句按照指定的字段進(jìn)行排序是查詢數(shù)據(jù)時(shí)是一個(gè)很常見的操作。當(dāng)涉及到大量數(shù)據(jù)時(shí),對(duì)于 ORDER BY 操作,可以考慮為相應(yīng)的列添加索引,如果不使用索引,mysql會(huì)使用filesort來進(jìn)行排序。
filesort
filesort雖然有file,但是不一定是文件排序,要分情況。下面來看下排序的具體邏輯。
filesort會(huì)將查詢行數(shù)據(jù)放入sort_buffer中,然后按排序字段進(jìn)行排序。sort_buffer的大小有變量sort_buffer_size來控制,默認(rèn)大小256kb。
mysql> SELECT @@sort_buffer_size; +--------------------+ | @@sort_buffer_size | +--------------------+ | 262144 |
如果要排序的數(shù)據(jù)內(nèi)容小于sort_buffer_size,排序在內(nèi)存中即可完成;否則filesort會(huì)使用臨時(shí)文件進(jìn)行排序。數(shù)據(jù)越多生成的臨時(shí)文件越多,每份文件單獨(dú)排序后再歸并合并成一個(gè)有序的結(jié)果。
臨時(shí)文件存放再 tmpdir 變量指定的目錄下,排序完成后會(huì)自動(dòng)刪除。
mysql> select @@tmpdir; +----------+ | @@tmpdir | +----------+ | /tmp |
如果使用了filesort,在explain的Extra會(huì)顯示:Using filesort。是否使用了臨時(shí)文件還需要根據(jù)具體的執(zhí)行過程來判斷。下面通過information_schema.OPTIMIZER_TRACE表來查看是否使用臨時(shí)文件。
1、準(zhǔn)備一個(gè)t_user表,首先開啟optimizer_trace
mysql> SET optimizer_trace='enabled=on';
2、然后執(zhí)行查看explain這里再Extra會(huì)有Using filesort。
mysql> explain select * from t_user order by username; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 2700 | 100.00 | Using filesort | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ 1 row in set, 1 warning (0.00 sec)
3、執(zhí)行查詢
select * from t_user order by username;
注意這里是執(zhí)行查詢,不是explain,否則下面一步看不到信息
4、查看OPTIMIZER_TRACE
SELECT * FROM information_schema.OPTIMIZER_TRACE;
OPTIMIZER_TRACE一共有4列:
QUERY:表示當(dāng)前查詢語句
TRACE: 包含查詢優(yōu)化器的trace信息,json格式
MISSING_BYTES_BEYOND_MAXMEM:丟失字節(jié)數(shù)。
在trace內(nèi)容中有“filesort_summary”部分是關(guān)于filesort處理信息,如上面查詢對(duì)應(yīng)的filesort_summary
mysql8:
"filesort_summary": { "memory_available": 262144, "key_size": 481, "row_size": 6591, "max_rows_per_buffer": 39, "num_rows_estimate": 2700, "num_rows_found": 2800, "num_initial_chunks_spilled_to_disk": 8, "peak_memory_used": 264192, "sort_algorithm": "std::sort", "sort_mode": "<fixed_sort_key, packed_additional_fields>" }
mysql5.7:
"filesort_summary": { "rows": 2800, "examined_rows": 2800, "number_of_tmp_files": 6, "sort_buffer_size": 261784, "sort_mode": "<sort_key, rowid>" }
這里看到5.7和8差別還是有點(diǎn)大,但是幾個(gè)主要的字段還是差不多的。這里以5.7的trace信息來看。
sort_buffer_size是sort_buffer的大小。
rows:數(shù)據(jù)行數(shù)
number_of_tmp_files:臨時(shí)文件數(shù),如果該值為0,則表示未使用臨時(shí)文件,sort_buffer夠用。
sort_mode:排序方式。這個(gè)指定了參與排序的數(shù)據(jù)內(nèi)容不同。
sort_key, rowid:sort_buffer中加載的數(shù)據(jù)只有排序字段(sort_key)和rowid,rowid用來排序后再回表查詢獲取行數(shù)據(jù)。
sort_key, additional_fields:sort_buffer中加載數(shù)據(jù)包含所有的要查詢的字段。
sort_key, packed_additional_fields:和上面的additional_fields差不多,只是有些可變長(zhǎng)度會(huì)進(jìn)行壓縮。
那么sort_mode這兩種類型一種有所有查詢字段,一種只有排序字段,查詢優(yōu)化器是根據(jù)什么選擇的呢。這里有一個(gè)參數(shù)max_length_for_sort_data,如果查詢的數(shù)據(jù)行記錄超過該值,則會(huì)采用sort_key, rowid模式,否則會(huì)采用sort_key, additional_fields模式。不過這個(gè)值在MySQL 8.0.20被標(biāo)為過時(shí)了,建議通過調(diào)整sort_buffer_size大小來控制join_buffer大小,盡量避免使用磁盤臨時(shí)文件。這兩種排序模式比較:additional_fields模式不需要回表,如果查詢的列比較多,可能會(huì)導(dǎo)致sort_buffer所能容納的行數(shù)據(jù)變少;rowid模式每行數(shù)據(jù)很小,sort_buffer可以加載更多的行,但是最后返回?cái)?shù)據(jù)要回表。不同數(shù)據(jù)量場(chǎng)景,要合理設(shè)置sort_buffer_size和max_length_for_sort_data搭配。
這里看到排序的過程,一行行拿出來進(jìn)行比較,數(shù)據(jù)量大還會(huì)使用到臨時(shí)文件,還是比較耗時(shí)的。那么有沒有更快的方法呢?那就是使用索引。
使用索引
為什么使用索引會(huì)快?因?yàn)闃?gòu)建后的索引就是天然有序的,不需要再經(jīng)過一行行逐一對(duì)sort_key進(jìn)行比較。跳過額外的filesort。
排序使用索引的情況一般在Extra中只有Using index。使用索引一般場(chǎng)景:
1、查詢條件中有排序索引
2、索引覆蓋,查詢的列都在對(duì)應(yīng)的索引中
3、多列索引排序,滿足最左匹配
4、排序方向一致
這只是一般的規(guī)則,下面結(jié)合幾個(gè)具體的例子來看看。
t_user表上有復(fù)合索引 (username,gender,department) 還有索引(phone)
例1:
SELECT uid,phone FROM t_user ORDER BY phone;
查詢列都在索引上(uid是主鍵),Using index
例2:
SELECT * FROM t_user ORDER BY phone;
索引不包含查詢?cè)L問的所有列,則僅當(dāng)索引訪問比其他訪問方法更高效時(shí)才使用該索引.這里查詢優(yōu)化器選擇了Using filesort。 不太理解?
例3:
SELECT uid,phone FROM t_user WHERE uid<20 ORDER BY phone;
雖然查詢內(nèi)容是索引覆蓋,但是where條件不在索引上(不是同一個(gè)索引),Using filesort。
這個(gè)應(yīng)該可以理解一個(gè)索引篩選出來的數(shù)據(jù)內(nèi)容對(duì)另一個(gè)索引是無序的。
例4:
SELECT username,gender FROM t_user ORDER BY username,gender; SELECT username,gender FROM t_user WHERE username='a' ORDER BY gender;
查詢內(nèi)容索引覆蓋,where滿足索引最左匹配部分,Using index。
例5:
SELECT username,gender FROM t_user WHERE username LIKE 'a%' ORDER BY username desc,gender ;
多列排序,第一列排序username滿足使用索引條件,gender和username排序方向相反,無法使用索引,會(huì)進(jìn)行依次filesort。
例6:
SELECT username,gender FROM t_user WHERE username LIKE 'a%' ORDER BY username,department ;
username排序使用filesort,department排序不滿足最左匹配,中間復(fù)合索引斷開(缺少gender)使用filesort。
這里只看了幾個(gè)常見例子,可能有些場(chǎng)景比這復(fù)雜的多。想一想能使用到索引排序一個(gè)最基本的條件:當(dāng)前要查詢的數(shù)據(jù)范圍(where篩選后)在排序列(對(duì)應(yīng)的索引)是有序的。然后才考慮覆蓋索引等其它需要滿足的條件。
到此這篇關(guān)于mysql order by 排序原理的文章就介紹到這了,更多相關(guān)mysql order by 排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySql如何查看數(shù)據(jù)庫(kù)變量信息常用腳本
這篇文章主要介紹了MySql如何查看數(shù)據(jù)庫(kù)變量信息常用腳本問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04Mysql使用insert插入多條記錄 批量新增數(shù)據(jù)
這篇文章主要介紹了Mysql使用insert插入多條記錄批量新增數(shù)據(jù),需要的朋友可以參考下2017-08-08mysql實(shí)現(xiàn)查詢最接近的記錄數(shù)據(jù)示例
這篇文章主要介紹了mysql實(shí)現(xiàn)查詢最接近的記錄數(shù)據(jù),涉及mysql查詢相關(guān)的時(shí)間轉(zhuǎn)換、排序等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07linux下perl操作mysql數(shù)據(jù)庫(kù)(需要安裝DBI)
有時(shí)候需要perl操作mysql數(shù)據(jù)庫(kù),可以通過DBI實(shí)現(xiàn),需要的朋友可以參考下2012-05-05