下列轉自:http://www.tech-q.cn/archiver/tid-11673.html
很多時候,我們在mysql中創建了索引,但是某些查詢還是很慢,根本就沒有使用到索引!一般來說,可能是某些字段沒有創建索引,或者是組合索引中字段的順序與查詢語句中字段的順序不符。
看下面的例子:
假設有一張訂單表(orders),包含order_id和product_id二個字段。
一共有31條數據。符合下面語句的數據有5條。執行下面的sql語句:
select product_id from orders where order_id in (123, 312, 223, 132, 224);
這條語句要mysql去根據order_id進行搜索,然后返回匹配記錄中的product_id。所以組合索引應該按照以下的順序創建:
create index orderid_productid on orders(order_id, product_id) mysql> explain select product_id from orders where order_id in (123, 312, 223, 132, 224) \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: orders type: range possible_keys: orderid_productid key: orderid_productid key_len: 5 ref: NULL rows: 5 Extra: Using where; Using index 1 row in set (0.00 sec)
可以看到,這個組合索引被用到了,掃描的范圍也很小,只有5行。如果把組合索引的順序換成product_id, order_id的話,mysql就會去索引中搜索 *123 *312 *223 *132 *224,必然會有些慢了。
mysql> create index orderid_productid on orders(product_id, order_id); Query OK, 31 rows affected (0.01 sec) Records: 31 Duplicates: 0 Warnings: 0 mysql> explain select product_id from orders where order_id in (123, 312, 223, 132, 224) \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: orders type: index possible_keys: NULL key: orderid_productid key_len: 10 ref: NULL rows: 31 Extra: Using where; Using index 1 row in set (0.00 sec)
這次索引搜索的性能顯然不能和上次相比了。rows:31,我的表中一共就31條數據。索引被使用部分的長度:key_len:10,比上一次的key_len:5多了一倍。不知道是這樣在索引里面查找速度快,還是直接去全表掃描更快呢?
mysql> alter table orders add modify_a char(255) default 'aaa'; Query OK, 31 rows affected (0.01 sec) Records: 31 Duplicates: 0 Warnings: 0 mysql> mysql> mysql> explain select modify_a from orders where order_id in (123, 312, 223, 132, 224) \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: orders type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 31 Extra: Using where 1 row in set (0.00 sec)
這樣就不會用到索引了。 剛才是因為select的product_id與where中的order_id都在索引里面的。
為什么要創建組合索引呢?這么簡單的情況直接創建一個order_id的索引不就行了嗎?果只有一個order_id索引,沒什么問題,會用到這個索引,然后mysql要去磁盤上的表里面取到product_id。如果有組合索引的話,mysql可以完全從索引中取到product_id,速度自然會快。再多說幾句組合索引的最左優先原則:
組合索引的第一個字段必須出現在查詢組句中,這個索引才會被用到。果有一個組合索引(col_a,col_b,col_c),下面的情況都會用到這個索引:
col_a = "some value"; col_a = "some value" and col_b = "some value"; col_a = "some value" and col_b = "some value" and col_c = "some value"; col_b = "some value" and col_a = "some value" and col_c = "some value";
對於最后一條語句,mysql會自動優化成第三條的樣子~~。下面的情況就不會用到索引:
col_b = "aaaaaa"; col_b = "aaaa" and col_c = "cccccc";
列轉自:http://hi.baidu.com/liuzhiqun/blog/item/4957bcb1aed1b5590823023c.html
通過實例理解單列索引、多列索引以及最左前綴原則。實例:現在我們想查出滿足以下條件的用戶id:
mysql>SELECT `uid` FROM people WHERE lname`='Liu' AND `fname`='Zhiqun' AND `age`=26
因為我們不想掃描整表,故考慮用索引。
單列索引:
ALTER TABLE people ADD INDEX lname (lname);
將lname列建索引,這樣就把范圍限制在lname='Liu'的結果集1上,之后掃描結果集1,產生滿足fname='Zhiqun'的結果集2,再掃描結果集2,找到 age=26的結果集3,即最終結果。
由 於建立了lname列的索引,與執行表的完全掃描相比,效率提高了很多,但我們要求掃描的記錄數量仍舊遠遠超過了實際所需 要的。雖然我們可以刪除lname列上的索引,再創建fname或者age 列的索引,但是,不論在哪個列上創建索引搜索效率仍舊相似。
2.多列索引:
ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age);
為了提高搜索效率,我們需要考慮運用多列索引,由於索引文件以B-Tree格式保存,所以我們不用掃描任何記錄,即可得到最終結果。
注:在mysql中執行查詢時,只能使用一個索引,如果我們在lname,fname,age上分別建索引,執行查詢時,只能使用一個索引,mysql會選擇一個最嚴格(獲得結果集記錄數最少)的索引。
3.最左前綴:顧名思義,就是最左優先,上例中我們創建了lname_fname_age多列索引,相當於創建了(lname)單列索引,(lname,fname)組合索引以及(lname,fname,age)組合索引。
注:在創建多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
建立索引的時機
到這里我們已經學會了建立索引,那么我們需要在什么情況下建立索引呢?一般來說,在WHERE和JOIN中出現的列需要建立索引,但也不完全如此,因為MySQL只對<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE才會使用索引。例如:
SELECT t.Name FROM mytable t LEFT JOIN mytable m ON t.Name=m.username WHERE m.age=20 AND m.city='鄭州'
此時就需要對city和age建立索引,由於mytable表的userame也出現在了JOIN子句中,也有對它建立索引的必要。
剛才提到只有某些時候的LIKE才需建立索引。因為在以通配符%和_開頭作查詢時,MySQL不會使用索引。例如下句會使用索引:
SELECT * FROM mytable WHERE username like'admin%'
下句就不會使用:
SELECT * FROM mytable WHEREt Name like'%admin'
因此,在使用LIKE時應注意以上的區別。
索引的不足之處
上面都在說使用索引的好處,但過多的使用索引將會造成濫用。因此索引也會有它的缺點:
- 雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件。
- 建立索引會占用磁盤空間的索引文件。一般情況這個問題不太嚴重,但如果你在一個大表上創建了多種組合索引,索引文件的會膨脹很快。索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。
使用索引的注意事項
使用索引時,有以下一些技巧和注意事項:
- 索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
- 使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
- 索引列排序
MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。
- like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
- 不要在列上進行運算
select * from users where YEAR(adddate)<2007;
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成
select * from users where adddate<‘2007-01-01’;
- 不使用NOT IN和<>操作