1. 程式人生 > >Mysql優化、索引

Mysql優化、索引

1,建立MySql索引 
對於查詢佔主要的應用來說,索引顯得尤為重要。很多時候效能問題很簡單的就是因為我們忘了新增索引而造成的,或者說沒有新增更為有效的索引導致。如果不加 
索引的話,那麼查詢任何哪怕只是一條特定的資料都會進行一次全表掃描,如果一張表的資料量很大而符合條件的結果又很少,那麼不加索引會引起致命的效能下 
降。但是也不是什麼情況都非得建索引不可,比如性別可能就只有兩個值,建索引不僅沒什麼優勢,還會影響到更新速度,這被稱為過度索引。 
2,複合索引 
比如有一條語句是這樣的:select * from users where area=’beijing’ and age=22; 
如果我們是在area和age上分別建立單個索引的話,由於mysql查詢每次只能使用一個索引,所以雖然這樣已經相對不做索引時全表掃描提高了很多效 
率,但是如果在area、age兩列上建立複合索引的話將帶來更高的效率。如果我們建立了(area, age, salary)的複合索引,那麼其實相當於建立了(area,age,salary)、(area,age)、(area)三個索引,這被稱為最佳左字首 特性。因此我們在建立複合索引時應該將最常用作限制條件的列放在最左邊,依次遞減。 
3,索引不會包含有NULL值的列 
只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。所以我們在資料庫設計時不要讓欄位的預設值為NULL。 
4,使用短索引 
對串列進行MySql索引,如果可能應該指定一個字首長度。例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字元內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁碟空間和I/O操作。 
5,排序的索引問題 
mysql查詢只使用一個MySql索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此資料庫預設排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列建立複合索引。 
6,like語句操作 
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。 
7,不要在列上進行運算 
select * from users where YEAR(adddate) 
8,不使用NOT IN和操作 
NOT IN和操作都不會使用索引將進行全表掃描。NOT IN可以NOT EXISTS代替,id3則可使用id>3 or id

mysql索引的不足之處
過多的使用索引將會造成濫用。因此索引也會有它的缺點:
◆雖然mysql索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行 INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要儲存資料,還要儲存一下索引檔案。
◆建立mysql索引會佔用磁碟空間的索 引檔案。一般情況這個問題不太嚴重,但如果你在一個大表上建立了多種組合索引,索引檔案的會膨脹很快。
索引只是提高效率的一個因素,如果你的 MySQL有大資料量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。
使用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和<>操作
如何檢視MySQL索引
mysql> show index from tblname;
mysql> show keys from tblname;
? Table
表的名稱。
? Non_unique
如果MySQL索引不能包括重複詞,則為0。如果可以,則為1。
? Key_name
索引的名稱。
? Seq_in_index
索引中的列序列號,從1開始。
? Column_name
列名稱。
? Collation
列以什麼方式儲存在索引中。在MySQL中,有值‘A’(升序)或NULL(無分類)。
? Cardinality
MySQL索引中唯一值的數目的估計值。通過執行ANALYZE TABLE或myisamchk -a可以更新。基數根據被儲存為整數的統計資料來計數,所以即使對於小型表,該值也沒有必要是精確的。基數越大,當進行聯合時,MySQL使用該索引的機會就越大。
? Sub_part
如果列只是被部分地編入索引,則為被編入索引的字元的數目。如果整列被編入索引,則為NULL。
? Packed
指示關鍵字如何被壓縮。如果沒有被壓縮,則為NULL。
? Null
如果列含有NULL,則含有YES。如果沒有,則該列含有NO。
? Index_type
用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
? Comment

關於建立索引的幾個準則:
1、合理的建立索引能夠加速資料讀取效率,不合理的建立索引反而會拖慢資料庫的響應速度。
2、索引越多,更新資料的速度越慢。
3、儘量在採用MyIsam作為引擎的時候使用索引(因為MySQL以BTree儲存索引),而不是InnoDB。但MyISAM不支援 Transcation。
4、當你的程式和資料庫結構/SQL語句已經優化到無法優化的程度,而程式瓶頸並不能順利解決,那就是應該考慮使用諸如memcached這樣的分 布式快取系統的時候了。
5、習慣和強迫自己用EXPLAIN來分析你SQL語句的效能。
一個很容易犯的錯誤:
不要在選擇的欄位上放置索引,這是無意義的。應該在條件選擇的語句上合理的放置索引,比如where,order by。
例子:
SELECT id,title,content,cat_id FROM article WHERE cat_id = 1;
上面這個語句,你在id/title/content上放置索引是毫無意義的,對這個語句沒有任何優化作用。但是如果你在外來鍵cat_id上放置一 個索引,那作用就相當大了。
幾個常用ORDER BY語句的MySQL優化:
1、ORDER BY + LIMIT組合的索引優化。如果一個SQL語句形如:
SELECT [column1],[column2],.... FROM [TABLE] ORDER BY [sort] LIMIT [offset],[LIMIT];
這個SQL語句優化比較簡單,在[sort]這個欄位上建立索引即可。
2、WHERE + ORDER BY + LIMIT組合的索引優化,形如:
SELECT [column1],[column2],.... FROM [TABLE] WHERE [columnX] = [value] ORDER BY [sort] LIMIT[offset],[LIMIT];
這個MySQL語句,如果你仍然採用第一個例子中建立索引的方法,雖然可以用到索引,但是效率不高。更高效的方法是建立一個聯合索引 (columnX,sort)
3、WHERE + IN + ORDER BY + LIMIT組合的索引優化,形如:
SELECT [column1],[column2],.... FROM [TABLE] WHERE [columnX] IN ([value1],[value2],...) ORDER BY[sort] LIMIT [offset],[LIMIT];
這個語句如果你採用第二個例子中建立索引的方法,會得不到預期的效果(僅在[sort]上是using index,WHERE那裡是using where;using filesort),理由是這裡對應columnX的值對應多個。
這個MySQL語句怎麼優化呢?我暫時沒有想到什麼好的辦法,看到網上有便宜提供的辦法,那就是將這個語句用UNION分拆,然後建立第二個例子中的索引:
SELECT [column1],[column2],.... FROM [TABLE] WHERE [columnX]=[value1] ORDER BY [sort] LIMIT[offset],[LIMIT] 
UNION 
SELECT [column1],[column2],.... FROM [TABLE] WHERE [columnX]=[value2] ORDER BY [sort] LIMIT[offset],[LIMIT] 
UNION 
……
但經驗證,這個方法根本行不通,效率反而更低,測試時對於大部分應用強制指定使用排序索引效果更好點
4、不要再WHERE和ORDER BY的欄位上應用表示式(函式),比如:
SELECT * FROM [table] ORDER BY YEAR(date) LIMIT 0,30;
5、WHERE+ORDER BY多個欄位+LIMIT,比如
SELECT * FROM [table] WHERE uid=1 ORDER x,y LIMIT 0,10;
對於這個語句,大家可能是加一個這樣的索引(x,y,uid)。但實際上更好的效果是(uid,x,y)。這是由MySQL處理排序的機制造成的。
以上例子你在實際專案中應用的時候,不要忘記在新增索引後,用EXPLAIN看看效果。