為了使這個查詢的效率更高,給其中一個聯結列添加索引並重新執行EXPLAIN語句:
MySQL> ALTER TABLE t2 ADD INDEX (i2);
MySQL> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index
我們可以看到性能提高了。T1的輸出沒有改變(表明還是需要進行全表掃描),但是優化器處理t2的方式就有所不同了:
· 類型從ALL改變為ref,意味著可以使用參考值(來自t1的值)來執行索引查找,定位t2中合格的數據行。
· 參考值在參考(ref)字段中給出了:sampdb.t1.i1。
· 行數值從1000降低到了10,顯示出優化器相信對於t1中的每一行,它只需要檢查t2中的10行(這是一個悲觀的估計值。實際上,在t2中只有一行與t1中數據行匹配。我們在後面會看到如何幫助優化器改善這個估計值)。數據行組合的全部估計值使1000×10=10000。它比前面的沒有索引的時候估計出來的一百萬好多了。
對t1進行索引有價值嗎?實際上,對於這個特定的聯結操作,掃描一張表是必要的,因此沒有必要對t1建立索引。如果你想看到效果,可以索引t1.i1並再次運行EXPLAIN:
MySQL> ALTER TABLE t1 ADD INDEX (i1);
MySQL> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index
上面的輸出與前面的EXPLAIN的輸出相似,但是添加索引對t1的輸出有一些改變。類型從NULL改成了index,附加(Extra)從空的改成了Using index。這些改變表明,盡管對索引的值仍然需要執行全表掃描,但是優化器還是可以直接從索引文件中讀取值,根據不需要使用數據文件。你可以從MyISAM表中看到這類結果,在這種情況下,優化器知道自己只詢問索引文件就能夠得到所有需要的信息。對於InnoDB 和BDB表也有這樣的結果,在這種情況下優化器可以單獨使用索引中的信息而不用搜索數據行。
我們可以運行ANALYZE TABLE使優化器進一步優化估計值。這會引起服務器生成鍵值的靜態分布。分析上面的表並再次運行EXPLAIN得到了更好的估計值:
MySQL> ANALYZE TABLE t1, t2;
MySQL> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 1
Extra: Using where; Using index
在這種情況下,優化器估計在t2中與t1的每個值匹配的數據行只有一個。
重載優化過程
這個過程聽起來多余,但是有時候你還是希望去掉某些MySQL優化行為的:
重載優化器的表聯結次序。使用STRAIGHT_JOIN強迫優化器按照特定的次序使用數據表。在這樣操作的時候,你必須對數據表進行排序,這樣才能保證第一張表是被選擇的行數最少的表。如果你不能確定被選擇行數最少的是哪一張表,那麼就把行數最多的放到第一的位置。換句話說,試著對表進行排序,使最有約束力的選擇出現在最前面。你對可能的備選數據行縮小地越早,執行查詢的性能就越好。請確保在帶有STRAIGHT_JOIN和不帶STRAIGHT_JOIN的時候分別執行該查詢。有時候由於某些原因的存在,優化器沒有按照你認定的方式聯結數據表,STRAIGHT_JOIN也可能沒有實際的幫助作用。
另一個可能性是在聯結的數據表列表中的某個表的後面使用FORCE INDEX、USE INDEX和IGNORE INDEX調節符來告訴MySQL如何使用索引。這在優化器沒有做出正確選擇的時候是有用處的。
以最小的代價清空一張表。當需要完全地清空一張MyISAM數據表的時候,最快的方法是刪除它並利用它的.frm文件中存儲的腳本來重新建立它。使用TRUNCATE TABLE語句實現:
TRUNCATE TABLE tbl_name;
通過重新建立MyISAM數據表來清空它的這種服務器優化措施使該操作非常快,因為不需要單獨地逐行刪除。
但是TRUNCATE TABLE也帶來了一些副作用,在某些環境中是不符合要求的:
· TRUNCATE TABLE不一定能夠計算出被刪除的數據列的精確數量。如果你需要這個數值,請使用不帶WHERE子句的DELETE語句:
DELETE FROM tbl_name;
· 但是,通過重新建立來清空數據表,它可能會把序號的起始值設置為1。為了避免這種情況,請使用"不優化的"全表DELETE語句,它帶有一個恆為真的WHERE子句:
DELETE FROM tbl_name WHERE 1;
添加WHERE子句會強迫MySQL進行逐行刪除,因為它必須計算出每一行的值來判斷是否能夠刪除它。這個語句執行的速度很慢,但是它卻保留了當前的AUTO_INCREMENT序號。