在 explain的幫助下,您就知道什么時(shí)候該給表添加索引,以使用索引來(lái)查找記錄從而讓select 運(yùn)行更快。
如果由于不恰當(dāng)使用索引而引起一些問(wèn)題的話(huà),可以運(yùn)行 analyze table來(lái)更新該表的統(tǒng)計(jì)信息,例如鍵的基數(shù),它能幫您在優(yōu)化方面做出更好的選擇。
explain 返回了一行記錄,它包括了 select語(yǔ)句中用到的各個(gè)表的信息。這些表在結(jié)果中按照mysql即將執(zhí)行的查詢(xún)中讀取的順序列出來(lái)。mysql用一次掃描多次連接(single- sweep,multi-join)的方法來(lái)解決連接。這意味著mysql從第一個(gè)表中讀取一條記錄,然后在第二個(gè)表中查找到對(duì)應(yīng)的記錄,然后在第三個(gè)表 中查找,依次類(lèi)推。當(dāng)所有的表都掃描完了,它輸出選擇的字段并且回溯所有的表,直到找不到為止,因?yàn)橛械谋碇锌赡苡卸鄺l匹配的記錄下一條記錄將從該表讀 取,再?gòu)南乱粋€(gè)表開(kāi)始繼續(xù)處理。
在mysql version 4.1中,explain輸出的結(jié)果格式改變了,使得它更適合例如 union語(yǔ)句、子查詢(xún)以及派生表的結(jié)構(gòu)。更令人注意的是,它新增了2個(gè)字段: id和 select_type。當(dāng)你使用早于mysql4.1的版本就看不到這些字段了。
explain結(jié)果的每行記錄顯示了每個(gè)表的相關(guān)信息,每行記錄都包含以下幾個(gè)字段:
id
本次 select 的標(biāo)識(shí)符。在查詢(xún)中每個(gè) select都有一個(gè)順序的數(shù)值。
select_type
select 的類(lèi)型,可能會(huì)有以下幾種:
simple: 簡(jiǎn)單的 select (沒(méi)有使用 union或子查詢(xún))
primary: 最外層的 select。
union: 第二層,在select 之后使用了 union。
dependent union: union 語(yǔ)句中的第二個(gè)select,依賴(lài)于外部子查詢(xún)
subquery: 子查詢(xún)中的第一個(gè) select
dependent subquery: 子查詢(xún)中的第一個(gè) subquery依賴(lài)于外部的子查詢(xún)
derived: 派生表 select(from子句中的子查詢(xún))
table
記錄查詢(xún)引用的表。
type
表連接類(lèi)型。以下列出了各種不同類(lèi)型的表連接,依次是從最好的到最差的:
system:表只有一行記錄(等于系統(tǒng)表)。這是 const表連接類(lèi)型的一個(gè)特例。
const:表中最多只有一行匹配的記錄,它在查詢(xún)一開(kāi)始的時(shí)候就會(huì)被讀取出來(lái)。由于只有一行記錄,在余下的優(yōu)化程序里該行記錄的字段值可以被當(dāng)作是一個(gè) 恒定值。const表查詢(xún)起來(lái)非常快,因?yàn)橹灰x取一次!const 用于在和 primary key 或unique 索引中有固定值比較的情形。下面的幾個(gè)查詢(xún)中,tbl_name 就是 c表了:
select * from tbl_name where primary_key=1; select * from tbl_namewhere primary_key_part1=1 and primary_key_part2=2;
eq_ref:從該表中會(huì)有一行記錄被讀取出來(lái)以和從前一個(gè)表中讀取出來(lái)的記錄做聯(lián)合。與const類(lèi)型不同的是,這是最好的連接類(lèi)型。它用在索引所有部 分都用于做連接并且這個(gè)索引是一個(gè)primary key 或 unique 類(lèi)型。eq_ref可以用于在進(jìn)行"="做比較時(shí)檢索字段。比較的值可以是固定值或者是表達(dá)式,表達(dá)示中可以使用表里的字段,它們?cè)谧x表之前已經(jīng)準(zhǔn)備好 了。以下的幾個(gè)例子中,mysql使用了eq_ref 連接來(lái)處理 ref_table:
select * from ref_table,other_table whereref_table.key_column=other_table.column; select * fromref_table,other_table whereref_table.key_column_part1=other_table.column andref_table.key_column_part2=1;
ref: 該表中所有符合檢索值的記錄都會(huì)被取出來(lái)和從上一個(gè)表中取出來(lái)的記錄作聯(lián)合。ref用于連接程序使用鍵的最左前綴或者是該鍵不是 primary key 或 unique索引(換句話(huà)說(shuō),就是連接程序無(wú)法根據(jù)鍵值只取得一條記錄)的情況。當(dāng)根據(jù)鍵值只查詢(xún)到少數(shù)幾條匹配的記錄時(shí),這就是一個(gè)不錯(cuò)的連接類(lèi)型。 ref還可以用于檢索字段使用 =操作符來(lái)比較的時(shí)候。以下的幾個(gè)例子中,mysql將使用 ref 來(lái)處理ref_table:
select * from ref_table where key_column=expr; select * fromref_table,other_table whereref_table.key_column=other_table.column; select * fromref_table,other_table whereref_table.key_column_part1=other_table.column andref_table.key_column_part2=1;
ref_or_null: 這種連接類(lèi)型類(lèi)似 ref,不同的是mysql會(huì)在檢索的時(shí)候額外的搜索包含null 值的記錄。這種連接類(lèi)型的優(yōu)化是從mysql4.1.1開(kāi)始的,它經(jīng)常用于子查詢(xún)。在以下的例子中,mysql使用ref_or_null 類(lèi)型來(lái)處理 ref_table:
select * from ref_table where key_column=expr or key_column is null;
unique_subquery: 這種類(lèi)型用例如一下形式的 in 子查詢(xún)來(lái)替換 ref:
value in (select primary_key from single_table where some_expr)
unique_subquery: 只是用來(lái)完全替換子查詢(xún)的索引查找函數(shù)效率更高了。
index_subquery: 這種連接類(lèi)型類(lèi)似 unique_subquery。它用子查詢(xún)來(lái)代替in,不過(guò)它用于在子查詢(xún)中沒(méi)有唯一索引的情況下,例如以下形式:
value in (select key_column from single_table where some_expr)
range: 只有在給定范圍的記錄才會(huì)被取出來(lái),利用索引來(lái)取得一條記錄。key字段表示使用了哪個(gè)索引。key_len字段包括了使用的鍵的最長(zhǎng)部分。這種類(lèi)型時(shí) ref 字段值是 null。range用于將某個(gè)字段和一個(gè)定植用以下任何操作符比較時(shí) =, <>, >,>=, <, <=, is null, <=>, between, 或 in:
select * from tbl_name where key_column = 10; select * fromtbl_name where key_column between 10 and 20; select * from tbl_namewhere key_column in (10,20,30); select * from tbl_name wherekey_part1= 10 and key_part2 in (10,20,30);
index: 連接類(lèi)型跟 all 一樣,不同的是它只掃描索引樹(shù)。它通常會(huì)比 all快點(diǎn),因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小。mysql在查詢(xún)的字段知識(shí)單獨(dú)的索引的一部分的情況下使用這種連接類(lèi)型。
all: 將對(duì)該表做全部掃描以和從前一個(gè)表中取得的記錄作聯(lián)合。這時(shí)候如果第一個(gè)表沒(méi)有被標(biāo)識(shí)為const的話(huà)就不大好了,在其他情況下通常是非常糟糕的。正常地,可以通過(guò)增加索引使得能從表中更快的取得記錄以避免all。
possible_keys
possible_keys字段是指 mysql在搜索表記錄時(shí)可能使用哪個(gè)索引。注意,這個(gè)字段完全獨(dú)立于explain 顯示的表順序。這就意味著 possible_keys里面所包含的索引可能在實(shí)際的使用中沒(méi)用到。如果這個(gè)字段的值是null,就表示沒(méi)有索引被用到。這種情況下,就可以檢查 where子句中哪些字段那些字段適合增加索引以提高查詢(xún)的性能。就這樣,創(chuàng)建一下索引,然后再用explain 檢查一下。詳細(xì)的查看章節(jié)"14.2.2 alter tablesyntax"。想看表都有什么索引,可以通過(guò) show index from tbl_name來(lái)看。
key
key字段顯示了mysql實(shí)際上要用的索引。當(dāng)沒(méi)有任何索引被用到的時(shí)候,這個(gè)字段的值就是null。想要讓mysql強(qiáng)行使用或者忽略在 possible_keys字段中的索引列表,可以在查詢(xún)語(yǔ)句中使用關(guān)鍵字force index, use index,或 ignore index。如果是 myisam 和 bdb 類(lèi)型表,可以使用 analyzetable 來(lái)幫助分析使用使用哪個(gè)索引更好。如果是 myisam類(lèi)型表,運(yùn)行命令 myisamchk --analyze也是一樣的效果。詳細(xì)的可以查看章節(jié)"14.5.2.1 analyze tablesyntax"和"5.7.2 table maintenance and crash recovery"。
key_len
key_len 字段顯示了mysql使用索引的長(zhǎng)度。當(dāng) key 字段的值為 null時(shí),索引的長(zhǎng)度就是 null。注意,key_len的值可以告訴你在聯(lián)合索引中mysql會(huì)真正使用了哪些索引。
ref
ref 字段顯示了哪些字段或者常量被用來(lái)和 key配合從表中查詢(xún)記錄出來(lái)。
rows
rows 字段顯示了mysql認(rèn)為在查詢(xún)中應(yīng)該檢索的記錄數(shù)。
extra
本字段顯示了查詢(xún)中mysql的附加信息。以下是這個(gè)字段的幾個(gè)不同值的解釋?zhuān)?br />
distinct:mysql當(dāng)找到當(dāng)前記錄的匹配聯(lián)合結(jié)果的第一條記錄之后,就不再搜索其他記錄了。
not exists:mysql在查詢(xún)時(shí)做一個(gè) left join優(yōu)化時(shí),當(dāng)它在當(dāng)前表中找到了和前一條記錄符合 left join條件后,就不再搜索更多的記錄了。下面是一個(gè)這種類(lèi)型的查詢(xún)例子:
select * from t1 left join t2 on t1.id=t2.id where t2.id isnull;
假使 t2.id 定義為 not null。這種情況下,mysql將會(huì)掃描表 t1并且用 t1.id 的值在 t2 中查找記錄。當(dāng)在 t2中找到一條匹配的記錄時(shí),這就意味著 t2.id 肯定不會(huì)都是null,就不會(huì)再在 t2 中查找相同 id值的其他記錄了。也可以這么說(shuō),對(duì)于 t1 中的每個(gè)記錄,mysql只需要在t2 中做一次查找,而不管在 t2 中實(shí)際有多少匹配的記錄。
range checked for each record (index map: #)
mysql沒(méi)找到合適的可用的索引。取代的辦法是,對(duì)于前一個(gè)表的每一個(gè)行連接,它會(huì)做一個(gè)檢驗(yàn)以決定該使用哪個(gè)索引(如果有的話(huà)),并且使用這個(gè)索引來(lái)從表里取得記錄。這個(gè)過(guò)程不會(huì)很快,但總比沒(méi)有任何索引時(shí)做表連接來(lái)得快。
using filesort: mysql需要額外的做一遍從而以排好的順序取得記錄。排序程序根據(jù)連接的類(lèi)型遍歷所有的記錄,并且將所有符合 where條件的記錄的要排序的鍵和指向記錄的指針存儲(chǔ)起來(lái)。這些鍵已經(jīng)排完序了,對(duì)應(yīng)的記錄也會(huì)按照排好的順序取出來(lái)。詳情請(qǐng)看"7.2.9how mysql optimizes order by"。
using index
字段的信息直接從索引樹(shù)中的信息取得,而不再去掃描實(shí)際的記錄。這種策略用于查詢(xún)時(shí)的字段是一個(gè)獨(dú)立索引的一部分。
using temporary: mysql需要?jiǎng)?chuàng)建臨時(shí)表存儲(chǔ)結(jié)果以完成查詢(xún)。這種情況通常發(fā)生在查詢(xún)時(shí)包含了groupby 和 order by 子句,它以不同的方式列出了各個(gè)字段。
using where
where子句將用來(lái)限制哪些記錄匹配了下一個(gè)表或者發(fā)送給客戶(hù)端。除非你特別地想要取得或者檢查表種的所有記錄,否則的話(huà)當(dāng)查詢(xún)的extra 字段值不是 using where 并且表連接類(lèi)型是 all 或 index時(shí)可能表示有問(wèn)題。
如果你想要讓查詢(xún)盡可能的快,那么就應(yīng)該注意 extra 字段的值為usingfilesort 和 using temporary 的情況。
你可以通過(guò) explain 的結(jié)果中 rows字段的值的乘積大概地知道本次連接表現(xiàn)如何。它可以粗略地告訴我們mysql在查詢(xún)過(guò)程中會(huì)查詢(xún)多少條記錄。如果是使用系統(tǒng)變量 max_join_size 來(lái)取得查詢(xún)結(jié)果,這個(gè)乘積還可以用來(lái)確定會(huì)執(zhí)行哪些多表select 語(yǔ)句。
下面的例子展示了如何通過(guò) explain提供的信息來(lái)較大程度地優(yōu)化多表聯(lián)合查詢(xún)的性能。
假設(shè)有下面的 select 語(yǔ)句,正打算用 explain 來(lái)檢測(cè):
explain select tt.ticketnumber, tt.timein, tt.projectreference,tt.estimatedshipdate, tt.actualshipdate, tt.clientid,tt.servicecodes, tt.repetitiveid, tt.currentprocess,tt.currentdppers tt.recordvolume, tt.dpprinted, et.country,et_1.country, do.custname from tt, et, et as et_1, do wherett.submittime is null and tt.actualpc = et.employid andtt.assignedpc = et_1.employid and tt.clientid = do.custnmbr;
在這個(gè)例子中,先做以下假設(shè):
要比較的字段定義如下:
table column columntype
tt actualpc char(10)
tt assignedpc char(10)
tt clientid char(10)
et employid char(15)
do custnmbr char(15)
數(shù)據(jù)表的索引如下:
table index
tt actualpc
tt assignedpc
tt clientid
et employid (primary key)
do custnmbr (primary key)
tt.actualpc 的值是不均勻分布的。
在任何優(yōu)化措施未采取之前,經(jīng)過(guò) explain分析的結(jié)果顯示如下:
table type possible_keys key key_len ref rows extra
et all primarynull null null 74
do all primary null null null 2135
et_1 allprimary null null null 74
tt all assignedpc, null null null 3872 clientid, actualpc range checked for each record (key map: 35)
由于字段 type 的對(duì)于每個(gè)表值都是all,這個(gè)結(jié)果意味著mysql對(duì)所有的表做一個(gè)迪卡爾積;這就是說(shuō),每條記錄的組合。這將需要花很長(zhǎng)的時(shí)間,因?yàn)樾枰獟呙杳總€(gè)表總 記錄數(shù)乘積的總和。在這情況下,它的積是74 * 2135 * 74 * 3872 = 45,268,558,720條記錄。如果數(shù)據(jù)表更大的話(huà),你可以想象一下需要多長(zhǎng)的時(shí)間。
在這里有個(gè)問(wèn)題是當(dāng)字段定義一樣的時(shí)候,mysql就可以在這些字段上更快的是用索引(對(duì)isam類(lèi)型的表來(lái)說(shuō),除非字段定義完全一樣,否則不會(huì)使用索 引)。在這個(gè)前提下,varchar和 char是一樣的除非它們定義的長(zhǎng)度不一致。由于 tt.actualpc 定義為char(10),et.employid 定義為 char(15),二者長(zhǎng)度不一致。
為了解決這個(gè)問(wèn)題,需要用 alter table 來(lái)加大 actualpc的長(zhǎng)度從10到15個(gè)字符:
mysql> alter table tt modify actualpc varchar(15);
現(xiàn)在 tt.actualpc 和 et.employid 都是 varchar(15)
了。再來(lái)執(zhí)行一次 explain 語(yǔ)句看看結(jié)果:
table type possible_keys key key_len ref rows extra
tt allassignedpc, null null null 3872 using clientid, where actualpc
do all primary null null null 2135 range checked for each record (keymap: 1)
et_1 all primary null null null 74 range checked for eachrecord (key map: 1) et eq_ref primary primary 15 tt.actualpc 1
這還不夠,它還可以做的更好:現(xiàn)在 rows值乘積已經(jīng)少了74倍。這次查詢(xún)需要用2秒鐘。
第二個(gè)改變是消除在比較 tt.assignedpc = et_1.employid 和 tt.clientid= do.custnmbr 中字段的長(zhǎng)度不一致問(wèn)題:
mysql> alter table tt modify assignedpc varchar(15), ->modify clientid varchar(15);
現(xiàn)在 explain 的結(jié)果如下:
table type possible_keys key key_len ref rows extra
et all primary null null null 74
tt ref assignedpc, actualpc 15 et.employid 52 using clientid, where actualpc
et_1 eq_ref primary primary 15 tt.assignedpc 1
do eq_ref primary primary 15 tt.clientid 1
這看起來(lái)已經(jīng)是能做的最好的結(jié)果了。
遺留下來(lái)的問(wèn)題是,mysql默認(rèn)地認(rèn)為字段 tt.actualpc的值是均勻分布的,然而表 tt并非如此。幸好,我們可以很方便的讓mysql分析索引的分布:
mysql> analyze table tt;
到此為止,表連接已經(jīng)優(yōu)化的很完美了,explain 的結(jié)果如下:
table type possible_keys key key_len ref rows extra
tt all assignedpc null null null 3872 using clientid, where actualpc
et eq_ref primary primary 15 tt.actualpc 1
et_1 eq_ref primary primary 15 tt.assignedpc 1
do eq_ref primary primary 15 tt.clientid 1
請(qǐng)注意,explain 結(jié)果中的 rows字段的值也是mysql的連接優(yōu)化程序大致猜測(cè)的,請(qǐng)檢查這個(gè)值跟真實(shí)值是否基本一致。如果不是,可以通過(guò)在select 語(yǔ)句中使用 straight_join 來(lái)取得更好的性能,同時(shí)可以試著在from分句中用不同的次序列出各個(gè)表。
如果由于不恰當(dāng)使用索引而引起一些問(wèn)題的話(huà),可以運(yùn)行 analyze table來(lái)更新該表的統(tǒng)計(jì)信息,例如鍵的基數(shù),它能幫您在優(yōu)化方面做出更好的選擇。
explain 返回了一行記錄,它包括了 select語(yǔ)句中用到的各個(gè)表的信息。這些表在結(jié)果中按照mysql即將執(zhí)行的查詢(xún)中讀取的順序列出來(lái)。mysql用一次掃描多次連接(single- sweep,multi-join)的方法來(lái)解決連接。這意味著mysql從第一個(gè)表中讀取一條記錄,然后在第二個(gè)表中查找到對(duì)應(yīng)的記錄,然后在第三個(gè)表 中查找,依次類(lèi)推。當(dāng)所有的表都掃描完了,它輸出選擇的字段并且回溯所有的表,直到找不到為止,因?yàn)橛械谋碇锌赡苡卸鄺l匹配的記錄下一條記錄將從該表讀 取,再?gòu)南乱粋€(gè)表開(kāi)始繼續(xù)處理。
在mysql version 4.1中,explain輸出的結(jié)果格式改變了,使得它更適合例如 union語(yǔ)句、子查詢(xún)以及派生表的結(jié)構(gòu)。更令人注意的是,它新增了2個(gè)字段: id和 select_type。當(dāng)你使用早于mysql4.1的版本就看不到這些字段了。
explain結(jié)果的每行記錄顯示了每個(gè)表的相關(guān)信息,每行記錄都包含以下幾個(gè)字段:
id
本次 select 的標(biāo)識(shí)符。在查詢(xún)中每個(gè) select都有一個(gè)順序的數(shù)值。
select_type
select 的類(lèi)型,可能會(huì)有以下幾種:
simple: 簡(jiǎn)單的 select (沒(méi)有使用 union或子查詢(xún))
primary: 最外層的 select。
union: 第二層,在select 之后使用了 union。
dependent union: union 語(yǔ)句中的第二個(gè)select,依賴(lài)于外部子查詢(xún)
subquery: 子查詢(xún)中的第一個(gè) select
dependent subquery: 子查詢(xún)中的第一個(gè) subquery依賴(lài)于外部的子查詢(xún)
derived: 派生表 select(from子句中的子查詢(xún))
table
記錄查詢(xún)引用的表。
type
表連接類(lèi)型。以下列出了各種不同類(lèi)型的表連接,依次是從最好的到最差的:
system:表只有一行記錄(等于系統(tǒng)表)。這是 const表連接類(lèi)型的一個(gè)特例。
const:表中最多只有一行匹配的記錄,它在查詢(xún)一開(kāi)始的時(shí)候就會(huì)被讀取出來(lái)。由于只有一行記錄,在余下的優(yōu)化程序里該行記錄的字段值可以被當(dāng)作是一個(gè) 恒定值。const表查詢(xún)起來(lái)非常快,因?yàn)橹灰x取一次!const 用于在和 primary key 或unique 索引中有固定值比較的情形。下面的幾個(gè)查詢(xún)中,tbl_name 就是 c表了:
select * from tbl_name where primary_key=1; select * from tbl_namewhere primary_key_part1=1 and primary_key_part2=2;
eq_ref:從該表中會(huì)有一行記錄被讀取出來(lái)以和從前一個(gè)表中讀取出來(lái)的記錄做聯(lián)合。與const類(lèi)型不同的是,這是最好的連接類(lèi)型。它用在索引所有部 分都用于做連接并且這個(gè)索引是一個(gè)primary key 或 unique 類(lèi)型。eq_ref可以用于在進(jìn)行"="做比較時(shí)檢索字段。比較的值可以是固定值或者是表達(dá)式,表達(dá)示中可以使用表里的字段,它們?cè)谧x表之前已經(jīng)準(zhǔn)備好 了。以下的幾個(gè)例子中,mysql使用了eq_ref 連接來(lái)處理 ref_table:
select * from ref_table,other_table whereref_table.key_column=other_table.column; select * fromref_table,other_table whereref_table.key_column_part1=other_table.column andref_table.key_column_part2=1;
ref: 該表中所有符合檢索值的記錄都會(huì)被取出來(lái)和從上一個(gè)表中取出來(lái)的記錄作聯(lián)合。ref用于連接程序使用鍵的最左前綴或者是該鍵不是 primary key 或 unique索引(換句話(huà)說(shuō),就是連接程序無(wú)法根據(jù)鍵值只取得一條記錄)的情況。當(dāng)根據(jù)鍵值只查詢(xún)到少數(shù)幾條匹配的記錄時(shí),這就是一個(gè)不錯(cuò)的連接類(lèi)型。 ref還可以用于檢索字段使用 =操作符來(lái)比較的時(shí)候。以下的幾個(gè)例子中,mysql將使用 ref 來(lái)處理ref_table:
select * from ref_table where key_column=expr; select * fromref_table,other_table whereref_table.key_column=other_table.column; select * fromref_table,other_table whereref_table.key_column_part1=other_table.column andref_table.key_column_part2=1;
ref_or_null: 這種連接類(lèi)型類(lèi)似 ref,不同的是mysql會(huì)在檢索的時(shí)候額外的搜索包含null 值的記錄。這種連接類(lèi)型的優(yōu)化是從mysql4.1.1開(kāi)始的,它經(jīng)常用于子查詢(xún)。在以下的例子中,mysql使用ref_or_null 類(lèi)型來(lái)處理 ref_table:
select * from ref_table where key_column=expr or key_column is null;
unique_subquery: 這種類(lèi)型用例如一下形式的 in 子查詢(xún)來(lái)替換 ref:
value in (select primary_key from single_table where some_expr)
unique_subquery: 只是用來(lái)完全替換子查詢(xún)的索引查找函數(shù)效率更高了。
index_subquery: 這種連接類(lèi)型類(lèi)似 unique_subquery。它用子查詢(xún)來(lái)代替in,不過(guò)它用于在子查詢(xún)中沒(méi)有唯一索引的情況下,例如以下形式:
value in (select key_column from single_table where some_expr)
range: 只有在給定范圍的記錄才會(huì)被取出來(lái),利用索引來(lái)取得一條記錄。key字段表示使用了哪個(gè)索引。key_len字段包括了使用的鍵的最長(zhǎng)部分。這種類(lèi)型時(shí) ref 字段值是 null。range用于將某個(gè)字段和一個(gè)定植用以下任何操作符比較時(shí) =, <>, >,>=, <, <=, is null, <=>, between, 或 in:
select * from tbl_name where key_column = 10; select * fromtbl_name where key_column between 10 and 20; select * from tbl_namewhere key_column in (10,20,30); select * from tbl_name wherekey_part1= 10 and key_part2 in (10,20,30);
index: 連接類(lèi)型跟 all 一樣,不同的是它只掃描索引樹(shù)。它通常會(huì)比 all快點(diǎn),因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小。mysql在查詢(xún)的字段知識(shí)單獨(dú)的索引的一部分的情況下使用這種連接類(lèi)型。
all: 將對(duì)該表做全部掃描以和從前一個(gè)表中取得的記錄作聯(lián)合。這時(shí)候如果第一個(gè)表沒(méi)有被標(biāo)識(shí)為const的話(huà)就不大好了,在其他情況下通常是非常糟糕的。正常地,可以通過(guò)增加索引使得能從表中更快的取得記錄以避免all。
possible_keys
possible_keys字段是指 mysql在搜索表記錄時(shí)可能使用哪個(gè)索引。注意,這個(gè)字段完全獨(dú)立于explain 顯示的表順序。這就意味著 possible_keys里面所包含的索引可能在實(shí)際的使用中沒(méi)用到。如果這個(gè)字段的值是null,就表示沒(méi)有索引被用到。這種情況下,就可以檢查 where子句中哪些字段那些字段適合增加索引以提高查詢(xún)的性能。就這樣,創(chuàng)建一下索引,然后再用explain 檢查一下。詳細(xì)的查看章節(jié)"14.2.2 alter tablesyntax"。想看表都有什么索引,可以通過(guò) show index from tbl_name來(lái)看。
key
key字段顯示了mysql實(shí)際上要用的索引。當(dāng)沒(méi)有任何索引被用到的時(shí)候,這個(gè)字段的值就是null。想要讓mysql強(qiáng)行使用或者忽略在 possible_keys字段中的索引列表,可以在查詢(xún)語(yǔ)句中使用關(guān)鍵字force index, use index,或 ignore index。如果是 myisam 和 bdb 類(lèi)型表,可以使用 analyzetable 來(lái)幫助分析使用使用哪個(gè)索引更好。如果是 myisam類(lèi)型表,運(yùn)行命令 myisamchk --analyze也是一樣的效果。詳細(xì)的可以查看章節(jié)"14.5.2.1 analyze tablesyntax"和"5.7.2 table maintenance and crash recovery"。
key_len
key_len 字段顯示了mysql使用索引的長(zhǎng)度。當(dāng) key 字段的值為 null時(shí),索引的長(zhǎng)度就是 null。注意,key_len的值可以告訴你在聯(lián)合索引中mysql會(huì)真正使用了哪些索引。
ref
ref 字段顯示了哪些字段或者常量被用來(lái)和 key配合從表中查詢(xún)記錄出來(lái)。
rows
rows 字段顯示了mysql認(rèn)為在查詢(xún)中應(yīng)該檢索的記錄數(shù)。
extra
本字段顯示了查詢(xún)中mysql的附加信息。以下是這個(gè)字段的幾個(gè)不同值的解釋?zhuān)?br />
distinct:mysql當(dāng)找到當(dāng)前記錄的匹配聯(lián)合結(jié)果的第一條記錄之后,就不再搜索其他記錄了。
not exists:mysql在查詢(xún)時(shí)做一個(gè) left join優(yōu)化時(shí),當(dāng)它在當(dāng)前表中找到了和前一條記錄符合 left join條件后,就不再搜索更多的記錄了。下面是一個(gè)這種類(lèi)型的查詢(xún)例子:
select * from t1 left join t2 on t1.id=t2.id where t2.id isnull;
假使 t2.id 定義為 not null。這種情況下,mysql將會(huì)掃描表 t1并且用 t1.id 的值在 t2 中查找記錄。當(dāng)在 t2中找到一條匹配的記錄時(shí),這就意味著 t2.id 肯定不會(huì)都是null,就不會(huì)再在 t2 中查找相同 id值的其他記錄了。也可以這么說(shuō),對(duì)于 t1 中的每個(gè)記錄,mysql只需要在t2 中做一次查找,而不管在 t2 中實(shí)際有多少匹配的記錄。
range checked for each record (index map: #)
mysql沒(méi)找到合適的可用的索引。取代的辦法是,對(duì)于前一個(gè)表的每一個(gè)行連接,它會(huì)做一個(gè)檢驗(yàn)以決定該使用哪個(gè)索引(如果有的話(huà)),并且使用這個(gè)索引來(lái)從表里取得記錄。這個(gè)過(guò)程不會(huì)很快,但總比沒(méi)有任何索引時(shí)做表連接來(lái)得快。
using filesort: mysql需要額外的做一遍從而以排好的順序取得記錄。排序程序根據(jù)連接的類(lèi)型遍歷所有的記錄,并且將所有符合 where條件的記錄的要排序的鍵和指向記錄的指針存儲(chǔ)起來(lái)。這些鍵已經(jīng)排完序了,對(duì)應(yīng)的記錄也會(huì)按照排好的順序取出來(lái)。詳情請(qǐng)看"7.2.9how mysql optimizes order by"。
using index
字段的信息直接從索引樹(shù)中的信息取得,而不再去掃描實(shí)際的記錄。這種策略用于查詢(xún)時(shí)的字段是一個(gè)獨(dú)立索引的一部分。
using temporary: mysql需要?jiǎng)?chuàng)建臨時(shí)表存儲(chǔ)結(jié)果以完成查詢(xún)。這種情況通常發(fā)生在查詢(xún)時(shí)包含了groupby 和 order by 子句,它以不同的方式列出了各個(gè)字段。
using where
where子句將用來(lái)限制哪些記錄匹配了下一個(gè)表或者發(fā)送給客戶(hù)端。除非你特別地想要取得或者檢查表種的所有記錄,否則的話(huà)當(dāng)查詢(xún)的extra 字段值不是 using where 并且表連接類(lèi)型是 all 或 index時(shí)可能表示有問(wèn)題。
如果你想要讓查詢(xún)盡可能的快,那么就應(yīng)該注意 extra 字段的值為usingfilesort 和 using temporary 的情況。
你可以通過(guò) explain 的結(jié)果中 rows字段的值的乘積大概地知道本次連接表現(xiàn)如何。它可以粗略地告訴我們mysql在查詢(xún)過(guò)程中會(huì)查詢(xún)多少條記錄。如果是使用系統(tǒng)變量 max_join_size 來(lái)取得查詢(xún)結(jié)果,這個(gè)乘積還可以用來(lái)確定會(huì)執(zhí)行哪些多表select 語(yǔ)句。
下面的例子展示了如何通過(guò) explain提供的信息來(lái)較大程度地優(yōu)化多表聯(lián)合查詢(xún)的性能。
假設(shè)有下面的 select 語(yǔ)句,正打算用 explain 來(lái)檢測(cè):
explain select tt.ticketnumber, tt.timein, tt.projectreference,tt.estimatedshipdate, tt.actualshipdate, tt.clientid,tt.servicecodes, tt.repetitiveid, tt.currentprocess,tt.currentdppers tt.recordvolume, tt.dpprinted, et.country,et_1.country, do.custname from tt, et, et as et_1, do wherett.submittime is null and tt.actualpc = et.employid andtt.assignedpc = et_1.employid and tt.clientid = do.custnmbr;
在這個(gè)例子中,先做以下假設(shè):
要比較的字段定義如下:
table column columntype
tt actualpc char(10)
tt assignedpc char(10)
tt clientid char(10)
et employid char(15)
do custnmbr char(15)
數(shù)據(jù)表的索引如下:
table index
tt actualpc
tt assignedpc
tt clientid
et employid (primary key)
do custnmbr (primary key)
tt.actualpc 的值是不均勻分布的。
在任何優(yōu)化措施未采取之前,經(jīng)過(guò) explain分析的結(jié)果顯示如下:
table type possible_keys key key_len ref rows extra
et all primarynull null null 74
do all primary null null null 2135
et_1 allprimary null null null 74
tt all assignedpc, null null null 3872 clientid, actualpc range checked for each record (key map: 35)
由于字段 type 的對(duì)于每個(gè)表值都是all,這個(gè)結(jié)果意味著mysql對(duì)所有的表做一個(gè)迪卡爾積;這就是說(shuō),每條記錄的組合。這將需要花很長(zhǎng)的時(shí)間,因?yàn)樾枰獟呙杳總€(gè)表總 記錄數(shù)乘積的總和。在這情況下,它的積是74 * 2135 * 74 * 3872 = 45,268,558,720條記錄。如果數(shù)據(jù)表更大的話(huà),你可以想象一下需要多長(zhǎng)的時(shí)間。
在這里有個(gè)問(wèn)題是當(dāng)字段定義一樣的時(shí)候,mysql就可以在這些字段上更快的是用索引(對(duì)isam類(lèi)型的表來(lái)說(shuō),除非字段定義完全一樣,否則不會(huì)使用索 引)。在這個(gè)前提下,varchar和 char是一樣的除非它們定義的長(zhǎng)度不一致。由于 tt.actualpc 定義為char(10),et.employid 定義為 char(15),二者長(zhǎng)度不一致。
為了解決這個(gè)問(wèn)題,需要用 alter table 來(lái)加大 actualpc的長(zhǎng)度從10到15個(gè)字符:
mysql> alter table tt modify actualpc varchar(15);
現(xiàn)在 tt.actualpc 和 et.employid 都是 varchar(15)
了。再來(lái)執(zhí)行一次 explain 語(yǔ)句看看結(jié)果:
table type possible_keys key key_len ref rows extra
tt allassignedpc, null null null 3872 using clientid, where actualpc
do all primary null null null 2135 range checked for each record (keymap: 1)
et_1 all primary null null null 74 range checked for eachrecord (key map: 1) et eq_ref primary primary 15 tt.actualpc 1
這還不夠,它還可以做的更好:現(xiàn)在 rows值乘積已經(jīng)少了74倍。這次查詢(xún)需要用2秒鐘。
第二個(gè)改變是消除在比較 tt.assignedpc = et_1.employid 和 tt.clientid= do.custnmbr 中字段的長(zhǎng)度不一致問(wèn)題:
mysql> alter table tt modify assignedpc varchar(15), ->modify clientid varchar(15);
現(xiàn)在 explain 的結(jié)果如下:
table type possible_keys key key_len ref rows extra
et all primary null null null 74
tt ref assignedpc, actualpc 15 et.employid 52 using clientid, where actualpc
et_1 eq_ref primary primary 15 tt.assignedpc 1
do eq_ref primary primary 15 tt.clientid 1
這看起來(lái)已經(jīng)是能做的最好的結(jié)果了。
遺留下來(lái)的問(wèn)題是,mysql默認(rèn)地認(rèn)為字段 tt.actualpc的值是均勻分布的,然而表 tt并非如此。幸好,我們可以很方便的讓mysql分析索引的分布:
mysql> analyze table tt;
到此為止,表連接已經(jīng)優(yōu)化的很完美了,explain 的結(jié)果如下:
table type possible_keys key key_len ref rows extra
tt all assignedpc null null null 3872 using clientid, where actualpc
et eq_ref primary primary 15 tt.actualpc 1
et_1 eq_ref primary primary 15 tt.assignedpc 1
do eq_ref primary primary 15 tt.clientid 1
請(qǐng)注意,explain 結(jié)果中的 rows字段的值也是mysql的連接優(yōu)化程序大致猜測(cè)的,請(qǐng)檢查這個(gè)值跟真實(shí)值是否基本一致。如果不是,可以通過(guò)在select 語(yǔ)句中使用 straight_join 來(lái)取得更好的性能,同時(shí)可以試著在from分句中用不同的次序列出各個(gè)表。