MySQL學習筆記(2)——索引

索引簡介


是什麼:

幫助MySQL高效獲取數據的數據結構(B樹結構)

目的:

1、提高查詢效率
2、爲字段排序

缺點:

降低了insert、update、delete的速度。因爲不僅僅要保存數據,還要更新索引。

索引分類


單值索引:

一個索引是一個單列(數據庫表中的一列),一個表中可以有多個單列索引。

唯一索引:

索引列的值必須唯一,但可以爲null。

複合索引:

一個索引包含了多個列。

索引性能分析(explain)


索引性能分析離不開explain語句,具體語法如下:

Explain select * from table where columns= xxxx;

返回結果是一張列表:

id select_type table type possible_keys key keylen ref rows extra

id
代表表的讀取順序,一個查詢語句可能會聯合多張表進行查詢,此時id代表MySQL加載表的先後順序。id爲爲數值類型,數值越大代表優先級越高,則越先加載。當數值相同時,代表優先級相同,加載順序爲列表的顯示順序。

select_type
表示查詢類型。具體有如下類型:
SIMPLE:普通查詢即簡單的select查詢,查詢中不包含子查詢或者UNION查詢。

PRIMARY:在嵌套查詢中,最外層的查詢會被標記爲PRIMARY。

SUBQUERY:與PRIMARY相對的,在嵌套查詢的內層會被標記爲SUBQUERY。注意,這裏的SUBQUERY的子查詢是不能在from語句後面的查詢。

SELECT *
FROM `student`
WHERE id = (SELECT id
			FROM class
			WHERE name = '二班');

在上面查詢中,where後面的的查詢語句就會被標記成SUBQUERY。

DERIVED:同樣也是標記子查詢的符號,但與SUBQUERY不同的是,DERIVED標記的查詢是在from語句後面的。from語句後面的表會有笛卡爾積的操作,因此,MySQL要遞歸的執行這些子查詢,把結果放在臨時表裏。

SELECT t2.*
FROM (
		SELECT t3.id
		FROM  t3
		WHERE t3.other_column = '123'
)s1, t2
WHERE s1.id = t2.id;

table
這一行是顯示關於哪一張表的。

type
type表示查詢使用了何種類型。有以下類型(從左至右越來越差):

System const eq_ref ref range index All

System:表示表只有一行
const:常量連接,表最多隻有一行匹配,通用用於主鍵或者唯一索引比較時。即將一個主鍵放置到where後面作爲條件查詢,mysql優化器就能把這次查詢優化轉化爲一個常量。
eq_ref:唯一性索引掃描。對於每隔索引建,表中只有一條記錄與之匹配。
ref:非唯一性索引掃描,表中可能有多個幾路與之匹配。
range:給定範圍檢索,就是檢索中where語句中的 between,in ,<,>等
index:遍歷整個索引數掃描
All:全表遍歷。
注意: 雖然index和All都是全表遍歷,但是index是在索引中讀取,All是在硬盤中讀取。

possible keys
顯示在這次查詢過程中可能用到的索引

key
實際用到的索引。

keylen
索引使用的字節數。

ref
顯示索引的哪一列被使用了,可能是一個常數。你寫列或者常量被用於查找索引上的值。

rows
MySQL 查詢優化器根據統計信息,估算 SQL 要查找到結果集需要掃描讀取的數據行數。這個值非常直觀顯示 SQL 的效率好壞, 原則上 rows 越少越好。

extra
using filesort:說明MySQL會使用一個外部索引排序。而不是按照表內的索引進行讀取。這種操作性能差,實際應該儘量避免。
using temporary:表示MySQL需要使用臨時表來存儲結果集,出現此情況性能也會打折扣。
using index:表示使用到了覆蓋索引,覆蓋索引指的是在一次查詢中僅僅查詢索引就能得到待查詢的字段,而不需要訪問表。即索引列包含了待查詢列。
using where:表示使用到了where條件。

索引優化


單表情況

建表如下(圖爲article表):
article表
現要查詢category_id爲1,且comments大於1的情況下,views最多的article。查詢語句如下:

SELECT id,author_id 
FROM `article`
WHERE category_id = 1 AND comments > 1
ORDER BY views DESC 
LIMIT 1;

使用explain來檢測上面的查詢語句得:
查詢結果
該語句出現了全表掃描All,和文件外部排序using filesort。這樣的語句對數據庫查詢性能是大打折扣的,因此要將該語句優化。具體怎麼優化呢?這裏就是要建立索引了。
可以看到,這裏的帥選條件是category_id,comments和views。那麼常理來說應該將這三個按照語句中的出現順序建立複合索引。但是,由於comments的帥選條件是大於,是一個範圍。如果將它建立索引的話,它後面的索引將會失效, 也就是說建立了category_id,comments和views的複合索引,但實際只會用到category_id和comments,views會失效。因此在此僅僅建立category_id和views的複合索引。建立索引後再用explain分析上述查詢語句。結果如下:
優化查詢結果分析

雙表情況

有如下兩個表(student表和class表):
student表
class表

內連接

若使用內連接,則SQL語句如下:

SELECT * FROM student INNER JOIN class 
ON student.class_id = class.id

對此語句的分析結果如下:
分析結果
在此處對student的class_id 添加索引即可。

左外連接

SQL語句如下:

SELECT * FROM student LEFT JOIN class
ON student.class_id = class.id

此時沒有任何索引,性能肯定不佳。左外連接的索引要建立在left join後的表上的相關列。在這個查詢語句中,索引就應該就立在class表的id列。建立索引後執行查詢分析可得下圖:
結果

索引優化法則(技巧)


1.儘量全值匹配

全值匹配,意思就是索引建啥我查啥。例如建立索引:

CREATE index idx_nap ON staffs(name,age,position);

那麼根據全值匹配法則,下列語句是較優的:

SELECT name FROM staffs WHERE name = 'sam';
SELECT name,age FROM staffs WHERE name = 'sam' AND age = 23;
SELECT name,age,position FROM staffs WHERE name = 'sam' AND age = 23 AND position = 'CEO';

在此法則下,不管是待查詢的列(select後的語句),還是檢索條件(where後的語句),所涉及的列都是索引中的列。

2.最佳左前綴法則

以上面的索引爲例,建立的複合索引順序是name,age,position。就像是開火車一樣,name是車頭,name不在後面的車廂就不會起作用。同樣的,age若不在,就會只剩車頭name,position會作爲車尾與火車脫節。因此以下的查詢會有一個或者多個索引失效(注意:最後一個索引有效):

SELECT name FROM staffs WHERE age = 23; //索引失效
SELECT name FROM staffs WHERE position = 'CEO'; //索引失效
SELECT age,position FROM staffs WHERE age = 23 AND position = 'CEO'; //索引失效
SELECT name,position FROM staffs WHERE name = 'sam' AND position = 'CEO'; //name索引生效,position索引失效
SELECT name,age,position FROM `staffs` WHERE age = 23 AND name = 'sam' AND position = 'CEO'//使用到索引,where後面順序可以顛倒。

3.不在建索引的列上做任何操作

接上表staffs可知,單獨爲name建立一個索引。那麼下列語句會使用到索引

SELECT * FROM staffs WHERE name = 'sam';

如果將其中where語句後面的條件改爲如下語句時,索引則會失效。

SELECT name FROM staffs WHERE LEFT(name,3) = 'sam'; //匹配name字段左邊三個字符符合sam的所有行 

4.範圍條件右邊的索引會失效

同樣是staffs表,建立的複合索引順序是name,age,position。如下語句則會使用到索引:

SELECT name,age,position FROM staffs WHERE name = 'sam' AND age = 23 AND position = 'CEO';

但是,將age條件改爲>23,那麼age後的position索引會失效。

SELECT name,age,position FROM staffs WHERE name = 'sam' AND age > 23 AND position = 'CEO';

在上面SQL中,name列的索引被用於查找,age列的索引被用於排序。而position列的索引會失效。

5.儘量使用覆蓋索引

同第一條法則

6.MySQL在使用!=或者<>會導致索引失效

不解釋,簡單。

7.is null和is not null無法使用索引

使用staffs表,建立name單值索引。

SELECT name FROM staffs WHERE name IS NULL;
SELECT name FROM staffs WHERE name IS NOT NULL;

8.like以通配符%開頭會導致索引失效

使用staffs表,建立name單值索引。以下語句會使索引失效。

SELECT name FROM staffs WHERE name LIKE '%sa%';
SELECT name FROM staffs WHERE name LIKE '%sa';

對第一句使用explain查詢得到分析結果如下:
結果
可見,使用到了全表掃描。但是在具體業務邏輯中有時必須要用到%xxx%的查詢條件,那麼該如何優化呢?解決方案是建立與name相關聯的複合索引,並使用覆蓋索引查詢(即查詢字段被複合索引包括且符合最佳左前綴法則)。這裏建立name,age複合索引。此時執行上面的查詢語句得到如下結果:
結果
可見,由全表掃描變爲了全索引掃描,在性能上比全表掃描好了許多。

9.字符串不加引號索引失效

使用staffs表,建立name單值索引,在針對name進行查詢時候,如下語句會導致索引失效:

SELECT name,age,position FROM staffs WHERE name = sam

該語句能夠正確查詢出結果,是因爲MySQL內部會爲sam字段做了類型轉換。而恰恰是類型轉換會使索引失效。所以記得加上引號。

10.用or關鍵詞會使得索引失效

SELECT name,age,position FROM staffs WHERE name = 'sam' or name = 'lisa';
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章