文章目錄
索引簡介
是什麼:
幫助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表):
現要查詢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表):
內連接
若使用內連接,則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';