1. 程式人生 > >mysql 複合索引 總結

mysql 複合索引 總結

對於複合索引:MySQL從左到右的使用索引中的欄位,一個查詢可以只使用索引中的一部份,但只能是最左側部分。例如索引是key index (a,b,c). 可以支援a | a,b| a,b,c 3種組合進行查詢,但不支援 b,c進行查詢 .當最左側欄位是常量引用時,索引就十分有效。下面用幾個例子對比查詢條件的不同對效能影響.

create table test(
a int,
b int,
c int,
KEY a(a,b,c)
);

優: select * from test where a=10 and b>50
差: select * from test where a50

優: select * from test where order by a
差: select * from test where order by b
差: select * from test where order by c

優: select * from test where a=10 order by a
優: select * from test where a=10 order by b
差: select * from test where a=10 order by c

優: select * from test where a>10 order by a
差: select * from test where a>10 order by b
差: select * from test where a>10 order by c

優: select * from test where a=10 and b=10 order by a
優: select * from test where a=10 and b=10 order by b
優: select * from test where a=10 and b=10 order by c

優: select * from test where a=10 and b=10 order by a
優: select * from test where a=10 and b>10 order by b
差: select * from test where a=10 and b>10 order by c

索引原則

1.索引越少越好
原因:主要在修改資料時,第個索引都要進行更新,降低寫速度。
2.最窄的欄位放在鍵的左邊
3.避免file sort排序,臨時表和表掃描.

於是上網查了下相關的資料:(關於複合索引優化的)

兩個或更多個列上的索引被稱作複合索引。
利用索引中的附加列,您可以縮小搜尋的範圍,但使用一個具有兩列的索引不同於使用兩個單獨的索引。複合索引的結構與電話簿類似,人名由姓和名構成,電話簿首先按姓氏對進行排序,然後按名字對有相同姓氏的人進行排序。如果您知道姓,電話簿將非常有用;如果您知道姓和名,電話簿則更為有用,但如果您只知道名不姓,電話簿將沒有用處。
所以說建立複合索引時,應該仔細考慮列的順序。對索引中的所有列執行搜尋或僅對前幾列執行搜尋時,複合索引非常有用;僅對後面的任意列執行搜尋時,複合索引則沒有用處。
如:建立 姓名、年齡、性別的複合索引。



複合索引的建立原則:

如果您很可能僅對一個列多次執行搜尋,則該列應該是複合索引中的第一列。如果您很可能對一個兩列索引中的兩個列執行單獨的搜尋,則應該建立另一個僅包含第二列的索引。
如上圖所示,如果查詢中需要對年齡和性別做查詢,則應當再新建一個包含年齡和性別的複合索引。
包含多個列的主鍵始終會自動以複合索引的形式建立索引,其列的順序是它們在表定義中出現的順序,而不是在主鍵定義中指定的順序。在考慮將來通過主鍵執行的搜尋,確定哪一列應該排在最前面。
請注意,建立複合索引應當包含少數幾個列,並且這些列經常在select查詢裡使用。在複合索引裡包含太多的列不僅不會給帶來太多好處。而且由於使用相當多的記憶體來儲存複合索引的列的值,其後果是記憶體溢位和效能降低。

         
複合索引對排序的優化:

複合索引只對和索引中排序相同或相反的order by 語句優化。
在建立複合索引時,每一列都定義了升序或者是降序。如定義一個複合索引:

Sql程式碼  收藏程式碼
  1. CREATE INDEX idx_example   
  2. ON table1 (col1 ASC, col2 DESC, col3 ASC)  

其中 有三列分別是:col1 升序,col2 降序, col3 升序。現在如果我們執行兩個查詢
1:Select col1, col2, col3 from table1 order by col1 ASC, col2 DESC, col3 ASC
  和索引順序相同
2:Select col1, col2, col3 from table1 order by col1 DESC, col2 ASC, col3 DESC
 和索引順序相反
查詢1,2 都可以別複合索引優化。
如果查詢為:
Select col1, col2, col3 from table1 order by col1 ASC, col2 ASC, col3 ASC
  排序結果和索引完全不同時,此時的查詢不會被複合索引優化。


查詢優化器在在where查詢中的作用:

如果一個多列索引存在於 列 Col1 和 Col2 上,則以下語句:Select   * from table where   col1=val1 AND col2=val2 查詢優化器會試圖通過決定哪個索引將找到更少的行。之後用得到的索引去取值。
1. 如果存在一個多列索引,任何最左面的索引字首能被優化器使用。所以聯合索引的順序不同,影響索引的選擇,儘量將值少的放在前面。
如:一個多列索引為 (col1 ,col2, col3)
    那麼在索引在列 (col1) 、(col1 col2) 、(col1 col2 col3) 的搜尋會有作用。
Sql程式碼  收藏程式碼
  1. SELECT * FROM tb WHERE  col1 = val1  
  2. SELECT * FROM tb WHERE  col1 = val1 and col2 = val2  
  3. SELECT * FROM tb WHERE  col1 = val1 and col2 = val2  AND col3 = val3  


2. 如果列不構成索引的最左面字首,則建立的索引將不起作用。
如:
Sql程式碼  收藏程式碼
  1. SELECT * FROM  tb WHERE  col3 = val3  
  2. SELECT * FROM  tb  WHERE  col2 = val2  
  3. SELECT * FROM  tb  WHERE  col2 = val2  and  col3=val3  

3. 如果一個 Like 語句的查詢條件不以萬用字元起始則使用索引。
如:%車 或 %車%   不使用索引。
    車%              使用索引。
索引的缺點:
1.       佔用磁碟空間。
2.       增加了插入和刪除的操作時間。一個表擁有的索引越多,插入和刪除的速度越慢。如 要求快速錄入的系統不宜建過多索引。

下面是一些常見的索引限制問題

1、使用不等於操作符(<>, !=)
下面這種情況,即使在列dept_id有一個索引,查詢語句仍然執行一次全表掃描
select * from dept where staff_num <> 1000;
但是開發中的確需要這樣的查詢,難道沒有解決問題的辦法了嗎?
有!
通過把用 or 語法替代不等號進行查詢,就可以使用索引,以避免全表掃描:上面的語句改成下面這樣的,就可以使用索引了。

Sql程式碼  收藏程式碼
  1. select * from dept shere staff_num < 1000 or dept_id > 1000;  


2、使用 is null 或 is not null
使用 is null 或is nuo null也會限制索引的使用,因為資料庫並沒有定義null值。如果被索引的列中有很多null,就不會使用這個索引(除非索引是一個位圖索引,關於點陣圖索引,會在以後的blog文章裡做詳細解釋)。在sql語句中使用null會造成很多麻煩。
解決這個問題的辦法就是:建表時把需要索引的列定義為非空(not null)

3、使用函式
如果沒有使用基於函式的索引,那麼where子句中對存在索引的列使用函式時,會使優化器忽略掉這些索引。下面的查詢就不會使用索引:
Sql程式碼  收藏程式碼
  1. select * from staff where trunc(birthdate) = '01-MAY-82';  

但是把函式應用在條件上,索引是可以生效的,把上面的語句改成下面的語句,就可以通過索引進行查詢。
Sql程式碼  收藏程式碼
  1. select * from staff where birthdate < (to_date('01-MAY-82') + 0.9999);  


4、比較不匹配的資料型別
比較不匹配的資料型別也是難於發現的效能問題之一。
下面的例子中,dept_id是一個varchar2型的欄位,在這個欄位上有索引,但是下面的語句會執行全表掃描。
Sql程式碼  收藏程式碼
  1. select * from dept where dept_id = 900198;  

這是因為oracle會自動把where子句轉換成to_number(dept_id)=900198,就是3所說的情況,這樣就限制了索引的使用。
把SQL語句改為如下形式就可以使用索引
Sql程式碼  收藏程式碼
  1. select * from dept where dept_id = '900198';  

還有就是參見 老王的blog上的文章