13 優(yōu)化SELECT語句和其它查詢

首先塘装,影響所有語句的一個因素是:你的許可設(shè)置得越復(fù)雜,所需要的開銷越多诚些。

執(zhí)行GRANT語句時使用簡單的許可飞傀,當(dāng)客戶執(zhí)行語句時皇型,可以使MySQL降低許可檢查開銷诬烹。例如,如果未授予任何表級或列級權(quán)限弃鸦,服務(wù)器不需要檢查tables_priv和columns_priv表的內(nèi)容绞吁。同樣地,如果不對任何 賬戶進(jìn)行限制唬格,服務(wù)器不需要對資源進(jìn)行統(tǒng)計(jì)。如果查詢量很高门粪,可以花一些時間使用簡化的授權(quán)結(jié)構(gòu)來降低許可檢查開銷玄妈。

如果你的問題是與具體MySQL表達(dá)式或函數(shù)有關(guān)髓梅,可以使用mysql客戶程序所帶的BENCHMARK()函數(shù)執(zhí)行定時測試枯饿。其語法為BENCHMARK(loop_count,expression)奢方。例如:

mysql> SELECT BENCHMARK(1000000,1+1);
+------------------------+
| BENCHMARK(1000000,1+1) |
+------------------------+
|                      0 |
+------------------------+
1 row in set (0.32 sec)

上面結(jié)果在PentiumII 400MHz系統(tǒng)上獲得阁谆。它顯示MySQL在該系統(tǒng)上在0.32秒內(nèi)可以執(zhí)行1,000,000個簡單的+表達(dá)式運(yùn)算场绿。

所有MySQL函數(shù)應(yīng)該被高度優(yōu)化嫉入,但是總有可能有一些例外咒林。BENCHMARK()是一個找出是否查詢有問題的優(yōu)秀的工具。

EXPLAIN語法(獲取SELECT相關(guān)信息)

EXPLAIN tbl_name
或:
EXPLAIN [EXTENDED] SELECT select_options

EXPLAIN語句可以用作DESCRIBE的一個同義詞澎粟,或獲得關(guān)于MySQL如何執(zhí)行SELECT語句的信息:

  • EXPLAIN tbl_name是DESCRIBE tbl_name或SHOW COLUMNS FROM tbl_name的一個同義詞欢瞪。

  • 如果在SELECT語句前放上關(guān)鍵詞EXPLAIN遣鼓,MySQL將解釋它如何處理SELECT骑祟,提供有關(guān)表如何聯(lián)接和聯(lián)接的次序。

EXPLAIN的第二個用法

借助于EXPLAIN潜圃,可以知道什么時候必須為表加入索引以得到一個使用索引來尋找記錄的更快的SELECT秉犹。

如果由于使用不正確的索引出現(xiàn)了問題崇堵,應(yīng)運(yùn)行ANALYZE TABLE更新表的統(tǒng)計(jì)(例如關(guān)鍵字集的勢),這樣會影響優(yōu)化器進(jìn)行的選擇赏廓。

還可以知道優(yōu)化器是否以一個最佳次序聯(lián)接表傍妒。為了強(qiáng)制優(yōu)化器讓一個SELECT語句按照表命名順序的聯(lián)接次序,語句應(yīng)以STRAIGHT_JOIN而不只是SELECT開頭颤练。

EXPLAIN為用于SELECT語句中的每個表返回一行信息。表以它們在處理查詢過程中將被MySQL讀入的順序被列出嗦玖。MySQL用一遍掃描多次聯(lián)接(single-sweep multi-join)的方式解決所有聯(lián)接。這意味著MySQL從第一個表中讀一行宇挫,然后找到在第二個表中的一個匹配行苛吱,然后在第3個表中等等器瘪。當(dāng)所有的表處理完后翠储,它輸出選中的列并且返回表清單直到找到一個有更多的匹配行的表。從該表讀入下一行并繼續(xù)處理下一個表橡疼。

當(dāng)使用EXTENDED關(guān)鍵字時,EXPLAIN產(chǎn)生附加信息,可以用SHOW WARNINGS瀏覽耻涛。該信息顯示優(yōu)化器限定SELECT語句中的表和列名,重寫并且執(zhí)行優(yōu)化規(guī)則后SELECT語句是什么樣子卓研,并且還可能包括優(yōu)化過程的其它注解。

EXPLAIN的每個輸出行提供一個表的相關(guān)信息,并且每個行包括下面的列:

  •     id
    

SELECT識別符磨淌。這是SELECT的查詢序列號疲憋。

  •      select_type
    

SELECT類型,可以為以下任何一種:


-         SIMPLE

簡單SELECT(不使用UNION或子查詢) 


-        PRIMARY

最外面的SELECT


-        UNION

UNION中的第二個或后面的SELECT語句


-         DEPENDENT UNION

UNION中的第二個或后面的SELECT語句梁只,取決于外面的查詢


-        UNION RESULT

UNION的結(jié)果缚柳。


-         SUBQUERY

子查詢中的第一個SELECT


-         DEPENDENT SUBQUERY

子查詢中的第一個SELECT,取決于外面的查詢



-         DERIVED

導(dǎo)出表的SELECT(FROM子句的子查詢)


  •      table
    

輸出的行所引用的表搪锣。

  •      type
    

聯(lián)接類型秋忙。下面給出各種聯(lián)接類型,按照從最佳類型到最壞類型進(jìn)行排序:


-        system

表僅有一行(=系統(tǒng)表)构舟。這是const聯(lián)接類型的一個特例灰追。


-        const

表最多有一個匹配行,它將在查詢開始時被讀取狗超。因?yàn)閮H有一行监嗜,在這行的列值可被優(yōu)化器剩余部分認(rèn)為是常數(shù)。const表很快抡谐,因?yàn)樗鼈冎蛔x取一次裁奇!

const用于用常數(shù)值比較PRIMARY 

KEY或UNIQUE索引的所有部分時。在下面的查詢中麦撵,tbl_name可以用于const表:

SELECT * from tbl_name WHERE primary_key=1刽肠;
 
SELECT * from tbl_name
WHERE primary_key_part1=1和 primary_key_part2=2;

-        eq_ref

對于每個來自于前面的表的行組合免胃,從該表中讀取一行音五。這可能是最好的聯(lián)接類型,除了const類型羔沙。它用在一個索引的所有部分被聯(lián)接使用并且索引是UNIQUE或PRIMARY KEY躺涝。

eq_ref可以用于使用= 操作符比較的帶索引的列。比較值可以為常量或一個使用在該表前面所讀取的表的列的表達(dá)式扼雏。


在下面的例子中坚嗜,MySQL可以使用eq_ref聯(lián)接來處理ref_tables:

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
    AND ref_table.key_column_part2=1;


-       ref

對于每個來自于前面的表的行組合夯膀,所有有匹配索引值的行將從這張表中讀取。如果聯(lián)接只使用鍵的最左邊的前綴苍蔬,或如果鍵不是UNIQUE或PRIMARY KEY(換句話說诱建,如果聯(lián)接不能基于關(guān)鍵字選擇單個行的話),則使用ref碟绑。如果使用的鍵僅僅匹配少量行俺猿,該聯(lián)接類型是不錯的。

ref可以用于使用=或<=>操作符的帶索引的列格仲。

在下面的例子中押袍,MySQL可以使用ref聯(lián)接來處理ref_tables:

SELECT * FROM ref_table WHERE key_column=expr;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
    AND ref_table.key_column_part2=1;

-       ref_or_null

該聯(lián)接類型如同ref,但是添加了MySQL可以專門搜索包含NULL值的行凯肋。在解決子查詢中經(jīng)常使用該聯(lián)接類型的優(yōu)化谊惭。

在下面的例子中,MySQL可以使用ref_or_null聯(lián)接來處理ref_tables:

SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;


-        index_merge

該聯(lián)接類型表示使用了索引合并優(yōu)化方法否过。在這種情況下午笛,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關(guān)鍵元素苗桂。


-        unique_subquery

該類型替換了下面形式的IN子查詢的ref:

value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery是一個索引查找函數(shù)药磺,可以完全替換子查詢,效率更高煤伟。

-          index_subquery

該聯(lián)接類型類似于unique_subquery癌佩。可以替換IN子查詢便锨,但只適合下列形式的子查詢中的非唯一索引:

value IN (SELECT key_column FROM single_table WHERE some_expr)

-          range

只檢索給定范圍的行围辙,使用一個索引來選擇行。key列顯示使用了哪個索引放案。key_len包含所使用索引的最長關(guān)鍵元素姚建。在該類型中ref列為NULL。

當(dāng)使用=吱殉、<>掸冤、>、>=友雳、<稿湿、<=、IS NULL押赊、<=>饺藤、BETWEEN或者IN操作符,用常量比較關(guān)鍵字列時,可以使用range:

SELECT * FROM tbl_name
WHERE key_column = 10;
 
SELECT * FROM tbl_name
WHERE key_column BETWEEN 10 and 20;
 
SELECT * FROM tbl_name
WHERE key_column IN (10,20,30);
 
SELECT * FROM tbl_name
WHERE key_part1= 10 AND key_part2 IN (10,20,30);

-          index

該聯(lián)接類型與ALL相同涕俗,除了只有索引樹被掃描罗丰。這通常比ALL快,因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小咽袜。

當(dāng)查詢只使用作為單索引一部分的列時丸卷,MySQL可以使用該聯(lián)接類型枕稀。


-          ALL

對于每個來自于先前的表的行組合询刹,進(jìn)行完整的表掃描。如果表是第一個沒標(biāo)記const的表萎坷,這通常不好凹联,并且通常在它情況下很差。通扯叩担可以增加更多的索引而不要使用ALL蔽挠,使得行能基于前面的表中的常數(shù)值或列值被檢索出。

  •     possible_keys
    

possible_keys列指出MySQL能使用哪個索引在該表中找到行瓜浸。注意澳淑,該列完全獨(dú)立于EXPLAIN輸出所示的表的次序。這意味著在possible_keys中的某些鍵實(shí)際上不能按生成的表次序使用插佛。

如果該列是NULL杠巡,則沒有相關(guān)的索引。在這種情況下雇寇,可以通過檢查WHERE子句看是否它引用某些列或適合索引的列來提高你的查詢性能氢拥。如果是這樣,創(chuàng)造一個適當(dāng)?shù)乃饕⑶以俅斡肊XPLAIN檢查查詢锨侯。

為了看清一張表有什么索引嫩海,使用SHOW INDEX FROM tbl_name。

  •     key
    

key列顯示MySQL實(shí)際決定使用的鍵(索引)囚痴。如果沒有選擇索引叁怪,鍵是NULL。要想強(qiáng)制MySQL使用或忽視possible_keys列中的索引深滚,在查詢中使用FORCE INDEX奕谭、USE INDEX或者IGNORE INDEX。

對于MyISAM和BDB表成箫,運(yùn)行ANALYZE TABLE可以幫助優(yōu)化器選擇更好的索引展箱。對于MyISAM表,可以使用myisamchk --analyze蹬昌。

  •       key_len
    

key_len列顯示MySQL決定使用的鍵長度混驰。如果鍵是NULL,則長度為NULL。注意通過key_len值我們可以確定MySQL將實(shí)際使用一個多部關(guān)鍵字的幾個部分栖榨。

  •       ref
    

ref列顯示使用哪個列或常數(shù)與key一起從表中選擇行昆汹。

  •       rows
    

rows列顯示MySQL認(rèn)為它執(zhí)行查詢時必須檢查的行數(shù)。

  •     Extra
    

該列包含MySQL解決查詢的詳細(xì)信息婴栽。下面解釋了該列可以顯示的不同的文本字符串:


-         Distinct

MySQL發(fā)現(xiàn)第1個匹配行后满粗,停止為當(dāng)前的行組合搜索更多的行。


-        Not exists

MySQL能夠?qū)Σ樵冞M(jìn)行LEFT JOIN優(yōu)化愚争,發(fā)現(xiàn)1個匹配LEFT JOIN標(biāo)準(zhǔn)的行后映皆,不再為前面的的行組合在該表內(nèi)檢查更多的行。

下面是一個可以這樣優(yōu)化的查詢類型的例子:

SELECT * 從t1 LEFT JOIN t2 ON t1.id=t2.id
  WHERE t2.id IS NULL轰枝;

假定t2.id定義為NOT NULL捅彻。在這種情況下,MySQL使用t1.id的值掃描t1并查找t2中的行鞍陨。如果MySQL在t2中發(fā)現(xiàn)一個匹配的行步淹,它知道t2.id絕不會為NULL,并且不再掃描t2內(nèi)有相同的id值的行诚撵。換句話說缭裆,對于t1的每個行,MySQL只需要在t2中查找一次寿烟,無論t2內(nèi)實(shí)際有多少匹配的行澈驼。


-         range checked for each record (index map: #)

MySQL沒有發(fā)現(xiàn)好的可以使用的索引,但發(fā)現(xiàn)如果來自前面的表的列值已知韧衣,可能部分索引可以使用盅藻。對前面的表的每個行組合,MySQL檢查是否可以使用range或index_merge訪問方法來索取行畅铭。

這并不很快氏淑,但比執(zhí)行沒有索引的聯(lián)接要快得多。


-        Using filesort

MySQL需要額外的一次傳遞硕噩,以找出如何按排序順序檢索行假残。通過根據(jù)聯(lián)接類型瀏覽所有行并為所有匹配WHERE子句的行保存排序關(guān)鍵字和行的指針來完成排序。然后關(guān)鍵字被排序炉擅,并按排序順序檢索行辉懒。


-         Using index

從只使用索引樹中的信息而不需要進(jìn)一步搜索讀取實(shí)際的行來檢索表中的列信息。當(dāng)查詢只使用作為單一索引一部分的列時谍失,可以使用該策略眶俩。

-       Using temporary

為了解決查詢,MySQL需要創(chuàng)建一個臨時表來容納結(jié)果快鱼。典型情況如查詢包含可以按不同情況列出列的GROUP BY和ORDER BY子句時颠印。


-        Using where

WHERE子句用于限制哪一個行匹配下一個表或發(fā)送到客戶纲岭。除非你專門從表中索取或檢查所有行,如果Extra值不為Using where并且表聯(lián)接類型為ALL或index线罕,查詢可能會有一些錯誤止潮。

如果想要使查詢盡可能快,應(yīng)找出Using filesort 和Using temporary的Extra值钞楼。

-        Using sort_union(...), Using union(...), Using intersect(...)

這些函數(shù)說明如何為index_merge聯(lián)接類型合并索引掃描


-        Using index for group-by

類似于訪問表的Using index方式喇闸,Using index for group-by表示MySQL發(fā)現(xiàn)了一個索引,可以用來查詢GROUP BY或DISTINCT查詢的所有列询件,而不要額外搜索硬盤訪問實(shí)際的表燃乍。并且,按最有效的方式使用索引雳殊,以便對于每個組橘沥,只讀取少量索引條目窗轩。

通過相乘EXPLAIN輸出的rows列的所有值夯秃,你能得到一個關(guān)于一個聯(lián)接如何的提示。這應(yīng)該粗略地告訴你MySQL必須檢查多少行以執(zhí)行查詢痢艺。當(dāng)你使用max_join_size變量限制查詢時仓洼,也用這個乘積來確定執(zhí)行哪個多表SELECT語句。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末堤舒,一起剝皮案震驚了整個濱河市色建,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舌缤,老刑警劉巖箕戳,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異国撵,居然都是意外死亡陵吸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門介牙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壮虫,“玉大人,你說我怎么就攤上這事环础∏羲疲” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵线得,是天一觀的道長饶唤。 經(jīng)常有香客問我,道長贯钩,這世上最難降的妖魔是什么募狂? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任呵晨,我火速辦了婚禮,結(jié)果婚禮上熬尺,老公的妹妹穿的比我還像新娘摸屠。我一直安慰自己,他們只是感情好粱哼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布季二。 她就那樣靜靜地躺著,像睡著了一般揭措。 火紅的嫁衣襯著肌膚如雪胯舷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天绊含,我揣著相機(jī)與錄音桑嘶,去河邊找鬼。 笑死躬充,一個胖子當(dāng)著我的面吹牛逃顶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播充甚,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼以政,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伴找?” 一聲冷哼從身側(cè)響起盈蛮,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎技矮,沒想到半個月后抖誉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衰倦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年袒炉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耿币。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡梳杏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淹接,到底是詐尸還是另有隱情十性,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布塑悼,位于F島的核電站劲适,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厢蒜。R本人自食惡果不足惜霞势,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一烹植、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愕贡,春花似錦草雕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憨琳,卻和暖如春诫钓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篙螟。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工菌湃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人遍略。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓惧所,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墅冷。 傳聞我的和親對象是個殘疾皇子纯路,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容