亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談MySQL排序原理與案例分析

 更新時(shí)間:2016年03月22日 08:59:21   投稿:hebedich  
這篇文章主要介紹了淺談MySQL排序原理與案例分析的相關(guān)資料,需要的朋友可以參考下

前言

排序是數(shù)據(jù)庫中的一個(gè)基本功能,MySQL也不例外。用戶通過Order by語句即能達(dá)到將指定的結(jié)果集排序的目的,其實(shí)不僅僅是Order by語句,Group by語句,Distinct語句都會(huì)隱含使用排序。本文首先會(huì)簡單介紹SQL如何利用索引避免排序代價(jià),然后會(huì)介紹MySQL實(shí)現(xiàn)排序的內(nèi)部原理,并介紹與排序相關(guān)的參數(shù),最后會(huì)給出幾個(gè)“奇怪”排序例子,來談?wù)勁判蛞恢滦詥栴},并說明產(chǎn)生現(xiàn)象的本質(zhì)原因。

1.排序優(yōu)化與索引使用

為了優(yōu)化SQL語句的排序性能,最好的情況是避免排序,合理利用索引是一個(gè)不錯(cuò)的方法。因?yàn)樗饕旧硪彩怯行虻?,如果在需要排序的字段上面建立了合適的索引,那么就可以跳過排序的過程,提高SQL的查詢速度。下面我通過一些典型的SQL來說明哪些SQL可以利用索引減少排序,哪些SQL不能。假設(shè)t1表存在索引key1(key_part1,key_part2),key2(key2)

a.可以利用索引避免排序的SQL

SELECT * FROM t1 ORDER BY key_part1,key_part2;
SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;
SELECT * FROM t1 WHERE key_part1 > constant ORDER BY key_part1 ASC;
SELECT * FROM t1 WHERE key_part1 = constant1 AND key_part2 > constant2 ORDER BY key_part2;

b.不能利用索引避免排序的SQL

//排序字段在多個(gè)索引中,無法使用索引排序
SELECT * FROM t1 ORDER BY key_part1,key_part2, key2;
 
//排序鍵順序與索引中列順序不一致,無法使用索引排序
SELECT * FROM t1 ORDER BY key_part2, key_part1;
 
//升降序不一致,無法使用索引排序
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
 
//key_part1是范圍查詢,key_part2無法使用索引排序
SELECT * FROM t1 WHERE key_part1> constant ORDER BY key_part2;

2.排序?qū)崿F(xiàn)的算法

對(duì)于不能利用索引避免排序的SQL,數(shù)據(jù)庫不得不自己實(shí)現(xiàn)排序功能以滿足用戶需求,此時(shí)SQL的執(zhí)行計(jì)劃中會(huì)出現(xiàn)“Using filesort”,這里需要注意的是filesort并不意味著就是文件排序,其實(shí)也有可能是內(nèi)存排序,這個(gè)主要由sort_buffer_size參數(shù)與結(jié)果集大小確定。MySQL內(nèi)部實(shí)現(xiàn)排序主要有3種方式,常規(guī)排序,優(yōu)化排序和優(yōu)先隊(duì)列排序,主要涉及3種排序算法:快速排序、歸并排序和堆排序。假設(shè)表結(jié)構(gòu)和SQL語句如下:

CREATE TABLE t1(id int, col1 varchar(64), col2 varchar(64), col3 varchar(64), PRIMARY KEY(id),key(col1,col2));
SELECT col1,col2,col3 FROM t1 WHERE col1>100 ORDER BY col2;

a.常規(guī)排序
(1).從表t1中獲取滿足WHERE條件的記錄
(2).對(duì)于每條記錄,將記錄的主鍵+排序鍵(id,col2)取出放入sort buffer
(3).如果sort buffer可以存放所有滿足條件的(id,col2)對(duì),則進(jìn)行排序;否則sort buffer滿后,進(jìn)行排序并固化到臨時(shí)文件中。(排序算法采用的是快速排序算法)
(4).若排序中產(chǎn)生了臨時(shí)文件,需要利用歸并排序算法,保證臨時(shí)文件中記錄是有序的
(5).循環(huán)執(zhí)行上述過程,直到所有滿足條件的記錄全部參與排序
(6).掃描排好序的(id,col2)對(duì),并利用id去撈取SELECT需要返回的列(col1,col2,col3)
(7).將獲取的結(jié)果集返回給用戶。
從上述流程來看,是否使用文件排序主要看sort buffer是否能容下需要排序的(id,col2)對(duì),這個(gè)buffer的大小由sort_buffer_size參數(shù)控制。此外一次排序需要兩次IO,一次是撈(id,col2),第二次是撈(col1,col2,col3),由于返回的結(jié)果集是按col2排序,因此id是亂序的,通過亂序的id去撈(col1,col2,col3)時(shí)會(huì)產(chǎn)生大量的隨機(jī)IO。對(duì)于第二次MySQL本身一個(gè)優(yōu)化,即在撈之前首先將id排序,并放入緩沖區(qū),這個(gè)緩存區(qū)大小由參數(shù)read_rnd_buffer_size控制,然后有序去撈記錄,將隨機(jī)IO轉(zhuǎn)為順序IO。
b.優(yōu)化排序
常規(guī)排序方式除了排序本身,還需要額外兩次IO。優(yōu)化的排序方式相對(duì)于常規(guī)排序,減少了第二次IO。主要區(qū)別在于,放入sort buffer不是(id,col2),而是(col1,col2,col3)。由于sort buffer中包含了查詢需要的所有字段,因此排序完成后可以直接返回,無需二次撈數(shù)據(jù)。這種方式的代價(jià)在于,同樣大小的sort buffer,能存放的(col1,col2,col3)數(shù)目要小于(id,col2),如果sort buffer不夠大,可能導(dǎo)致需要寫臨時(shí)文件,造成額外的IO。當(dāng)然MySQL提供了參數(shù)max_length_for_sort_data,只有當(dāng)排序元組小于max_length_for_sort_data時(shí),才能利用優(yōu)化排序方式,否則只能用常規(guī)排序方式。
c.優(yōu)先隊(duì)列排序
為了得到最終的排序結(jié)果,無論怎樣,我們都需要將所有滿足條件的記錄進(jìn)行排序才能返回。那么相對(duì)于優(yōu)化排序方式,是否還有優(yōu)化空間呢?5.6版本針對(duì)Order by limit M,N語句,在空間層面做了優(yōu)化,加入了一種新的排序方式--優(yōu)先隊(duì)列,這種方式采用堆排序?qū)崿F(xiàn)。堆排序算法特征正好可以解limit M,N 這類排序的問題,雖然仍然需要所有元素參與排序,但是只需要M+N個(gè)元組的sort buffer空間即可,對(duì)于M,N很小的場景,基本不會(huì)因?yàn)閟ort buffer不夠而導(dǎo)致需要臨時(shí)文件進(jìn)行歸并排序的問題。對(duì)于升序,采用大頂堆,最終堆中的元素組成了最小的N個(gè)元素,對(duì)于降序,采用小頂堆,最終堆中的元素組成了最大的N的元素。

3.排序不一致問題

案例1

Mysql從5.5遷移到5.6以后,發(fā)現(xiàn)分頁出現(xiàn)了重復(fù)值。
測試表與數(shù)據(jù):

create table t1(id int primary key, c1 int, c2 varchar(128));
insert into t1 values(1,1,'a');
insert into t1 values(2,2,'b');
insert into t1 values(3,2,'c');
insert into t1 values(4,2,'d');
insert into t1 values(5,3,'e');
insert into t1 values(6,4,'f');
insert into t1 values(7,5,'g');

假設(shè)每頁3條記錄,第一頁limit 0,3和第二頁limit 3,3查詢結(jié)果如下:

我們可以看到 id為4的這條記錄居然同時(shí)出現(xiàn)在兩次查詢中,這明顯是不符合預(yù)期的,而且在5.5版本中沒有這個(gè)問題。產(chǎn)生這個(gè)現(xiàn)象的原因就是5.6針對(duì)limit M,N的語句采用了優(yōu)先隊(duì)列,而優(yōu)先隊(duì)列采用堆實(shí)現(xiàn),比如上述的例子order by c1 asc limit 0,3 需要采用大小為3的大頂堆;limit 3,3需要采用大小為6的大頂堆。由于c1為2的記錄有3條,而堆排序是非穩(wěn)定的(對(duì)于相同的key值,無法保證排序后與排序前的位置一致),所以導(dǎo)致分頁重復(fù)的現(xiàn)象。為了避免這個(gè)問題,我們可以在排序中加上唯一值,比如主鍵id,這樣由于id是唯一的,確保參與排序的key值不相同。將SQL寫成如下:

select * from t1 order by c1,id asc limit 0,3;
select * from t1 order by c1,id asc limit 3,3;

案例2

兩個(gè)類似的查詢語句,除了返回列不同,其它都相同,但排序的結(jié)果不一致。
測試表與數(shù)據(jù):

create table t2(id int primary key, status int, c1 varchar(255),c2 varchar(255),c3 varchar(255),key(c1));
insert into t2 values(7,1,'a',repeat('a',255),repeat('a',255));
insert into t2 values(6,2,'b',repeat('a',255),repeat('a',255));
insert into t2 values(5,2,'c',repeat('a',255),repeat('a',255));
insert into t2 values(4,2,'a',repeat('a',255),repeat('a',255));
insert into t2 values(3,3,'b',repeat('a',255),repeat('a',255));
insert into t2 values(2,4,'c',repeat('a',255),repeat('a',255));
insert into t2 values(1,5,'a',repeat('a',255),repeat('a',255));

分別執(zhí)行SQL語句:

select id,status,c1,c2 from t2 force index(c1) where c1>='b' order by status;
select id,status from t2 force index(c1) where c1>='b' order by status;

執(zhí)行結(jié)果如下:

看看兩者的執(zhí)行計(jì)劃是否相同

為了說明問題,我在語句中加了force index的hint,確保能走上c1列索引。語句通過c1列索引撈取id,然后去表中撈取返回的列。根據(jù)c1列值的大小,記錄在c1索引中的相對(duì)位置如下:

(c1,id)===(b,6),(b,3),(5,c),(c,2),對(duì)應(yīng)的status值分別為2 3 2 4。從表中撈取數(shù)據(jù)并按status排序,則相對(duì)位置變?yōu)?6,2,b),(5,2,c),(3,3,c),(2,4,c),這就是第二條語句查詢返回的結(jié)果,那么為什么第一條查詢語句(6,2,b),(5,2,c)是調(diào)換順序的呢?這里要看我之前提到的a.常規(guī)排序和b.優(yōu)化排序中標(biāo)紅的部分,就可以明白原因了。由于第一條查詢返回的列的字節(jié)數(shù)超過了max_length_for_sort_data,導(dǎo)致排序采用的是常規(guī)排序,而在這種情況下MYSQL將rowid排序,將隨機(jī)IO轉(zhuǎn)為順序IO,所以返回的是5在前,6在后;而第二條查詢采用的是優(yōu)化排序,沒有第二次撈取數(shù)據(jù)的過程,保持了排序后記錄的相對(duì)位置。對(duì)于第一條語句,若想采用優(yōu)化排序,我們將max_length_for_sort_data設(shè)置調(diào)大即可,比如2048。

下面是本人關(guān)于mysql 自定義排序(field,INSTR,locate)的一點(diǎn)心得,希望對(duì)大家有所幫助
首先說明這里有三個(gè)函數(shù)(order by field,ORDER BY INSTR,ORDER BY locate)

原表:

 id user pass 
aaa aaa 
bbb bbb 
ccc ccc 
ddd ddd 
eee eee 
fff fff 

下面是我執(zhí)行后的結(jié)果:

SELECT * FROM `user` order by field(2,3,5,4,id) asc
id user pass 
aaa aaa 
ccc ccc 
ddd ddd 
eee eee 
fff fff 
bbb bbb 

根據(jù)結(jié)果分析:order by field(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 3 4 5 6 2

SELECT * FROM `user` order by field(2,3,5,4,id) desc
id user pass 
bbb bbb 
aaa aaa 
ccc ccc 
ddd ddd 
eee eee 
fff fff 

根據(jù)結(jié)果分析:order by field(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 1 3 4 5 6

SELECT * FROM `user` ORDER BY INSTR( '2,3,5,4', id ) ASC 
id user pass 
aaa aaa 
fff fff 
bbb bbb 
ccc ccc 
eee eee 
ddd ddd 

根據(jù)結(jié)果分析:order by INSTR(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 6 2 3 5 4

SELECT * FROM `user` ORDER BY INSTR( '2,3,5,4', id ) DESC
id user pass 
ddd ddd 
eee eee 
ccc ccc 
bbb bbb 
aaa aaa 
fff fff 

根據(jù)結(jié)果分析:order by INSTR(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 5 3 2 1 6

SELECT * FROM `user` ORDER BY locate( id, '2,3,5,4' ) ASC 

  id  user  pass 

aaa aaa 
fff fff 
bbb bbb 
ccc ccc 
eee eee 
ddd ddd

根據(jù)結(jié)果分析:order by locate(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 6 2 3 5 4

SELECT * FROM `user` ORDER BY locate( id, '2,3,5,4' ) DESC 
 id user pass 
ddd ddd 
eee eee 
ccc ccc 
bbb bbb 
aaa aaa 
fff fff 

根據(jù)結(jié)果分析:order by locate(2,3,5,4,1,6) 結(jié)果顯示順序?yàn)椋? 5 3 2 1 6
如我想要查找的數(shù)據(jù)庫中的ID順序首先是(2,3,5,4)然后在是其它的ID順序,你首先要把他降序排即(4 5 3 2),然后在 SELECT * FROM `user` ORDER BY INSTR( '4,5,3,2', id ) DESC limit 0,10 或用 SELECT *  FROM `user` ORDER BY locate( id, '4,5,3,2' ) DESC 就得到你想要的結(jié)果了。

 id user pass 
bbb bbb 
ccc ccc 
eee eee 
ddd ddd 
aaa aaa 
fff fff

相關(guān)文章

  • mysql5.6安裝配置方法圖文教程

    mysql5.6安裝配置方法圖文教程

    這篇文章主要為大家分享了mysql5.6安裝配置方法圖文教程,感興趣的朋友可以參考一下
    2016-06-06
  • MySQL之select in 子查詢優(yōu)化的實(shí)現(xiàn)

    MySQL之select in 子查詢優(yōu)化的實(shí)現(xiàn)

    這篇文章主要介紹了MySQL之select in 子查詢優(yōu)化的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 為什么MySQL分頁用limit會(huì)越來越慢

    為什么MySQL分頁用limit會(huì)越來越慢

    在mysql中l(wèi)imit可以實(shí)現(xiàn)快速分頁,但是如果數(shù)據(jù)到了幾百萬時(shí)我們的limit必須優(yōu)化才能有效的合理的實(shí)現(xiàn)分頁了,否則可能卡死你的服務(wù)器
    2021-07-07
  • 解決mysql ERROR 1017:Can''t find file: ''/xxx.frm'' 錯(cuò)誤

    解決mysql ERROR 1017:Can''t find file: ''/xxx.frm'' 錯(cuò)誤

    如果重啟服務(wù)器前沒有關(guān)閉mysql,MySql的MyiSAM表很有可能會(huì)出現(xiàn) ERROR #1017 :Can't find file: '/xxx.frm' 的錯(cuò)誤
    2011-08-08
  • MySQL如何讓一個(gè)表中可以有多個(gè)自增列

    MySQL如何讓一個(gè)表中可以有多個(gè)自增列

    這篇文章主要介紹了MySQL如何讓一個(gè)表中可以有多個(gè)自增列,自增列可使用?auto_increment?來實(shí)現(xiàn),當(dāng)一個(gè)列被標(biāo)識(shí)為?auto_increment?之后,在添加時(shí)如果不給此列設(shè)置任何值,或給此列設(shè)置?NULL?值時(shí),那么它會(huì)使用自增的規(guī)則來填充此列
    2022-06-06
  • mysql中的跨庫關(guān)聯(lián)查詢方法

    mysql中的跨庫關(guān)聯(lián)查詢方法

    這篇文章主要介紹了mysql中的跨庫關(guān)聯(lián)查詢方法,需要的朋友可以參考下
    2017-05-05
  • 實(shí)現(xiàn)mysql級(jí)聯(lián)復(fù)制的方法示例

    實(shí)現(xiàn)mysql級(jí)聯(lián)復(fù)制的方法示例

    這篇文章主要介紹了實(shí)現(xiàn)mysql級(jí)聯(lián)復(fù)制的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • MySQL Flink Watermark實(shí)現(xiàn)事件時(shí)間處理的關(guān)鍵技術(shù)

    MySQL Flink Watermark實(shí)現(xiàn)事件時(shí)間處理的關(guān)鍵技術(shù)

    Flink Watermark是Flink流處理框架中實(shí)現(xiàn)事件時(shí)間處理的關(guān)鍵技術(shù)之一。它是通過一種機(jī)制來解決數(shù)據(jù)亂序和延遲的問題,使得Flink可以在處理遲到的數(shù)據(jù)時(shí)保證結(jié)果的準(zhǔn)確性。Flink Watermark可以在數(shù)據(jù)流中插入一個(gè)時(shí)間戳,將數(shù)據(jù)流轉(zhuǎn)化為具有時(shí)間維度的數(shù)據(jù)集合
    2023-05-05
  • MySQL中使用去重distinct方法的示例詳解

    MySQL中使用去重distinct方法的示例詳解

    這篇文章主要介紹了MySQL中使用去重distinct方法的示例詳解。distinct用來查詢不重復(fù)記錄的條數(shù),distinct表示對(duì)后面的所有參數(shù)的拼接取 不重復(fù)的記錄,即查出的參數(shù)拼接每行記錄都是唯一的。地產(chǎn)感興趣的可以了解一下
    2020-07-07
  • MySQL主從原理及配置詳解

    MySQL主從原理及配置詳解

    這篇文章主要為大家詳細(xì)介紹了MySQL主從原理及配置的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03

最新評(píng)論