03.05 什麼是索引?索引為什麼快?


什麼是索引?索引為什麼快?

索引

索引是數據庫查詢操作中提升速度的一種手段,索引是一種數據結構。
索引是一個排序的列表,這個列表中存儲著索引的值和包含這個值的數據所在的物理地址,數據量龐大的時候,索引可以快速定位需要查找的數據對應的物理地址,不需要掃描全表的數據。

下面以MySQL為例帶你瞭解索引。

1. 建表時創建索引

<code>1
2
3
4
5
6
/<code>
<code>CREATE TABLE t_table(
ID INT NOT NULL,
USER_NAME VARCHAR(16) NOT NULL,
INDEX USER_NAME_INDEX (USER_NAME), #單列索引
INDEX (ID,USER_NAME) #組合索引
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT '註釋';
/<code>

2. 建表後創建索引

<code>1
2
3
/<code>
<code>ALTER TABLE t_TABLE ADD UNIQUE INDEX (ID); 

ALTER TABLE T_TABLE ADD INDEX (ID,USER_NAME);
ALTER TABLE T_TABLE ADD PRIMARY KEY (ID);
/<code>

3. 查看已經創建的索引

<code>1
/<code>
<code>show index from t_table;
/<code>

4. 刪除索引

<code>1
2
/<code>
<code>drop index user_name_index on t_table;
alter table t_table drop index user_name_index;
/<code>

5. 查看索引使用情況(執行計劃)

<code>1
/<code>
<code>explain select * from t_table where user_name = 'Tom';
/<code>
<code>1
2
3
4
5
6
7
/<code>
<code>mysql> explain select * from t_test where username = 'Tom';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | t_test | NULL | ref | t_test_index_username | t_test_index_username | 67 | const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
/<code>

說明:

id:SELECT識別符。這是SELECT的查詢序列號。

select_type:SELECT類型。

<code>SIMPLE:簡單SELECT(不使用UNION或子查詢)
PRIMARY:最外面的SELECT
UNION:UNION中的第二個或後面的SELECT語句
DEPENDENT UNION:UNION中的第二個或後面的SELECT語句,取決於外面的查詢
UNION RESULT:UNION的結果
SUBQUERY:子查詢中的第一個SELECT
DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢
DERIVED:導出表的SELECT(FROM子句的子查詢)/<code>

table:表名

type:聯接類型。是SQL性能的非常重要的一個指標,結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。
一般來說,得保證查詢至少達到range級別。

<code>system:表僅有一行(=系統表)。這是const聯接類型的一個特例。
const:表最多有一個匹配行,它將在查詢開始時被讀取。因為僅有一行,在這行的列值可被優化器剩餘部分認為是常數。const用於用常數值比較PRIMARY KEY或UNIQUE索引的所有部分時。
eq_ref:對於每個來自於前面的表的行組合,從該表中讀取一行。這可能是最好的聯接類型,除了const類型。它用在一個索引的所有部分被聯接使用並且索引是UNIQUE或PRIMARY KEY。eq_ref可以用於使用= 操作符比較的帶索引的列。比較值可以為常量或一個使用在該表前面所讀取的表的列的表達式。

ref:對於每個來自於前面的表的行組合,所有有匹配索引值的行將從這張表中讀取。如果聯接只使用鍵的最左邊的前綴,或如果鍵不是UNIQUE或PRIMARY KEY(換句話說,如果聯接不能基於關鍵字選擇單個行的話),則使用ref。如果使用的鍵僅僅匹配少量行,該聯接類型是不錯的。ref可以用於使用=或<=>操作符的帶索引的列。
ref_or_null:該聯接類型如同ref,但是添加了MySQL可以專門搜索包含NULL值的行。在解決子查詢中經常使用該聯接類型的優化。
index_merge:該聯接類型表示使用了索引合併優化方法。在這種情況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
unique_subquery:該類型替換了下面形式的IN子查詢的ref:value IN (SELECT primary_key FROMsingle_table WHERE some_expr);unique_subquery是一個索引查找函數,可以完全替換子查詢,效率更高。
index_subquery:該聯接類型類似於unique_subquery。可以替換IN子查詢,但只適合下列形式的子查詢中的非唯一索引:value IN (SELECT key_column FROM single_table WHERE some_expr)
range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引。key_len包含所使用索引的最長關鍵元素。在該類型中ref列為NULL。當使用=、<>、>、>=、、BETWEEN或者IN操作符,用常量比較關鍵字列時,可以使用range
index:該聯接類型與ALL相同,除了只有索引樹被掃描。這通常比ALL快,因為索引文件通常比數據文件小。

all:對於每個來自於先前的表的行組合,進行完整的表掃描。如果表是第一個沒標記const的表,這通常不好,並且通常在它情況下很差。通常可以增加更多的索引而不要使用ALL,使得行能基於前面的表中的常數值或列值被檢索出。/<code>

possible_keys:possible_keys列指出MySQL能使用哪個索引在該表中找到行。注意,該列完全獨立於EXPLAIN輸出所示的表的次序。這意味著在possible_keys中的某些鍵實際上不能按生成的表次序使用。

key:key列顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len:key_len列顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度為NULL。注意通過key_len值我們可以確定MySQL將實際使用一個多部關鍵字的幾個部分。

ref:ref列顯示使用哪個列或常數與key一起從表中選擇行。

rows:rows列顯示MySQL認為它執行查詢時必須檢查的行數。

Extra:該列包含MySQL解決查詢的詳細信息。

<code>Distinct:MySQL發現第1個匹配行後,停止為當前的行組合搜索更多的行。
Not exists:MySQL能夠對查詢進行LEFT JOIN優化,發現1個匹配LEFT JOIN標準的行後,不再為前面的的行組合在該表內檢查更多的行。

range checked for each record (index map: #):MySQL沒有發現好的可以使用的索引,但發現如果來自前面的表的列值已知,可能部分索引可以使用。對前面的表的每個行組合,MySQL檢查是否可以使用range或index_merge訪問方法來索取行。
Using filesort:MySQL需要額外的一次傳遞,以找出如何按排序順序檢索行。通過根據聯接類型瀏覽所有行併為所有匹配WHERE子句的行保存排序關鍵字和行的指針來完成排序。然後關鍵字被排序,並按排序順序檢索行。
Using index:從只使用索引樹中的信息而不需要進一步搜索讀取實際的行來檢索表中的列信息。當查詢只使用作為單一索引一部分的列時,可以使用該策略。
Using temporary:為了解決查詢,MySQL需要創建一個臨時表來容納結果。典型情況如查詢包含可以按不同情況列出列的GROUP BY和ORDER BY子句時。
Using where:WHERE子句用於限制哪一個行匹配下一個表或發送到客戶。除非你專門從表中索取或檢查所有行,如果Extra值不為Using where並且表聯接類型為ALL或index,查詢可能會有一些錯誤。
Using sort_union(...), Using union(...), Using intersect(...):這些函數說明如何為index_merge聯接類型合併索引掃描。
Using index for group-by:類似於訪問表的Using index方式,Using index for group-by表示MySQL發現了一個索引,可以用來查詢GROUP BY或DISTINCT查詢的所有列,而不要額外搜索硬盤訪問實際的表。並且,按最有效的方式使用索引,以便對於每個組,只讀取少量索引條目。/<code>

6. 模糊查詢時,%如果在前面,那麼不會使用索引。涉及到多個索引字段時,如果這些索引字段中,不存在主鍵索引的話,那麼就會使用該使用的索引。多個索引時,先使用哪個索引後使用哪個索引,是由MySQL的優化器經過一些列計算後作出的抉擇。當對索引字段進行 >, =, <=,not in,between …… and ……,函數(索引字段),like模糊查詢%在字段前時,不會使用該索引.在實際使用時,如果涉及到多列,我們一般都不會將這些列一 一創建為單列索引,而是將這些列創建為組合索引。

7. 組合索引的使用
最左原則
假設組合索引為:a,b,c的話;那麼當SQL中對應有:a或a,b或a,b,c的時候,可稱為完全滿足最左原則;當SQL中對應只有a,c的時候,可稱為部分滿足最左原則;當SQL中沒有a的時候,可稱為不滿足最左原則。
注:SQL語句中的對應條件的先後順序與創建組合索引中列的順序無關。如果完全滿足最左原則,所有的列都會走索引,部分滿足最左原則,那麼最左的列會走索引,剩下的不會走索引。不滿足最左原則的話就不會走索引。

8. 索引無法存儲null值

a. 單列索引無法儲null值,複合索引無法儲全為null的值。


b. 查詢時,採用is null條件時,不能利用到索引,只能全表掃描。
為什麼索引列無法存儲Null值?
a.索引是有序的。NULL值進入索引時,無法確定其應該放在哪裡。(將索引列值進行建樹,其中必然涉及到諸多的比較操作,null值是不確定值,無法比較,無法確定null出現在索引樹的葉子節點位置。) 
b.如果需要把空值存入索引,方法有二:其一,把NULL值轉為一個特定的值,在WHERE中檢索時,用該特定值查找。其二,建立一個複合索引。例如create index ind_a on table(col1,1);通過在複合索引中指定一個非空常量值,而使構成索引的列的組合中,不可能出現全空值。 

9. 不適合鍵值較少的列(重複數據較多的列)
假如索引列TYPE有5個鍵值,如果有1萬條數據,那麼WHERE TYPE = 1將訪問表中的2000個數據塊。再加上訪問索引塊,一共要訪問大於200個的數據塊。如果全表掃描,假設10條數據一個數據塊,那麼只需訪問1000個數據塊,既然全表掃描訪問的數據塊少一些,肯定就不會利用索引了。

10. 前導模糊查詢不能利用索引(like ‘%XX’或者like ‘%XX%’)
假如有這樣一列code的值為’AAA’,’AAB’,’BAA’,’BAB’ ,如果where code like '%AB'條件,由於前面是模糊的,所以不能利用索引的順序,必須一個個去找,看是否滿足條件。這樣會導致全索引掃描或者全表掃描。如果是這樣的條件where code like 'A%',就可以查找CODE中A開頭的CODE的位置,當碰到B開頭的數據時,就可以停止查找了,因為後面的數據一定不滿足要求。這樣就可以利用索引了。

11. 索引失效的幾種情況
a.如果條件中有or,即使其中有條件帶索引也不會使用(這也是為什麼儘量少用or的原因)要想使用or,又想讓索引生效,只能將or條件中的每個列都加上索引
b.對於多列索引,不是使用的第一部分,則不會使用索引
c.like查詢以%開頭
d.如果列類型是字符串,那一定要在條件中將數據使用引號引用起來,否則不使用索引
e.如果mysql估計使用全表掃描要比使用索引快,則不使用索引

12. MySQL主要提供2種方式的索引:B-Tree索引,Hash索引
B樹索引具有範圍查找和前綴查找的能力,對於有N節點的B樹,檢索一條記錄的複雜度為O(LogN)。相當於二分查找。哈希索引只能做等於查找,但是無論多大的Hash表,查找複雜度都是O(1)。
顯然,如果值的差異性大,並且以等值查找(=、 、in)為主,Hash索引是更高效的選擇,它有O(1)的查找複雜度。
如果值的差異性相對較差,並且以範圍查找為主,B樹是更好的選擇,它支持範圍查找。


分享到:


相關文章: