Innodb
存儲引擎的表數據和索引是存儲在同一個表空間裏面,在一定程度上索引的效率沒有MyISAM
快,但這絲毫不影響InnoDB
存儲引擎成爲主流的存儲引擎。
拓展:MyISAM
存儲引擎的表的數據和索引是分開存儲的,即每個 MyISAM
在磁盤上存儲 .frm(存儲表定義)、.MYD(存儲數據)、.MYI(存儲索引)
,數據和索引存放在不同的文件裏,獲取速度更快。
EXPLAIN 查看執行計劃信息的重點列
+----+-------------+-------+------+---------------+---------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+--------------------------+
| 1 | SIMPLE | user | ref | in_name | in_name | 93 | const | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+---------+---------+-------+------+--------------------------+
如上所示,explain
顯示了多列內容,其中type、possible_keys、key、key_len、ref、rows、Extra
是優化SQL
時必須要關注的。
-
type
:mysql
決定如何查找表中的行,下面是type的類型,從上到下性能由好到差NULL:MySQL在優化階段分解語句,在執行階段甚至不需要訪問表或者索引,例如從索引列中查找最小的索引通過單獨查找索引就可以完成,而不需要訪問表數據 system,const:通過主鍵查詢數據,只能返回一行數據,主鍵包括單列主鍵和聯合主鍵,聯合主鍵必須都存在,否則type列是ref類型,例如聯合主鍵(id,name),id和name都在where條件中才可以 eq_ref:MySQL最多隻會返回一行數據,待實驗 ref:使用索引查找,但不是主鍵和唯一索引,它可能會返回多個匹配的行 range:有範圍的索引掃描,即帶有 BETWEEN 或在 WHERE 子句裏面帶有 > 的查詢,有時候使用in或or也會顯示範圍掃描 index:全表掃描,但會按索引次序進行而不是行,避免了排序,缺點是按索引次序讀取數據整個表。如果在 Extra 列中看到了 Using index,說明使用的是覆蓋索引,只掃描索引的數據,而不是按照索引次序。 All:全表掃描
-
possible_keys
可能用到的索引 -
key
用到的索引 -
key_len
索引的長度。例如聯合索引(name,state,address)
,如果只使用name
部分的索引,那麼長度是name
部分索引長度,如果使用了name+state
部分索引,那麼長度是兩部分索引長度和。 -
ref
顯示key
列使用的索引中查找值所用的列或常量 -
rows
涉及的行數 -
Extra
# 索引相關 using index:使用了覆蓋索引 using where:在查找使用索引的情況下,需要回表查詢數據 using index condition:查找使用了索引,但是需要回表查詢查詢數據 using index ; using where:查找使用了索引,但是需要的數據都在索引列中能找到,所以不需要回表查詢 # 排序相關 using index:使用單列索引排序;使用聯合索引排序,且必須是聯合索引的首列在前,並且多列使用相同的排序。無所謂查詢的列是否只是索引列 using filesort:使用一般列排序或聯合索引的非首列 using index ; using filesort:多字段排序時,部分使用索引字段,部分是一般列;或聯合索引的多列時,非首列在在前;或聯合索引的多列時,首列在前但多列使用不同的排序 # 分組相關 using temporary ; using filesort:group by默認情況下會排序 using temporary :使用order by null優化後的情況 using index:使用索引分組
索引的使用和優化
-
是否使用索引與
where
後的條件順序無關 -
索引列使用比較符號,會使用索引,單列索引和聯合索引都可以,但是索引後面的索引不會被使用,聯合索引比較詳見第5條的案例
-
儘量使用聯合索引,減少使用單列索引。例如列
name、state、address
聯合索引(name,state,address)
相當於創建了三個索引:name、name+state、name+state+address
單列索引name、state、address
,當三個索引一起作爲查詢條件時,MySQL
會選擇一個最優的索引(數據的辨識度高的索引)來使用,並不會使用全部索引。
辨識度高是指根據索引查出的數據量小,例如數據庫中有100w
條數據,name
和state
都是單列索引,其中name='瞎子' and state = 0
的數據只有一條,其他數據均爲name='瞎子' and state = 1
,對於select * from table_name where name = '瞎子' and state = 0;
,MySQL
會使用索引status
-
索引全值匹配,例如聯合索引
(name,state,address)
,SQL:select * from table_name where state = '1' and name = '小喬' and address = '北京';
-
SQL
符合聯合索引的最左前綴法則,例如聯合索引(name ,state,address)
,要想用到索引,條件中必須要包括聯合索引中的第一個列,具體會用幾個索引參考下面的代碼# 用到三個列的索引 where name = '' and state = '' and address = '' # 用到兩個列的索引 where name = '' and state = '' # 用到name列的索引,因爲跳過了state列,所以address的列不會使用索引 where name = '' and address = '' # 用到name和state列的索引,address的列不會使用索引 where name = '' and state > '1' and address = '' # 沒有用到索引 where state = '' and address = ''
-
通常情況下,我們都會根據
where
條件來創建合適的索引,這是索引優化的一個方面,但數據庫通過索引查詢數據後要不要回表查詢,這也是索引優化的方面,也就是說MySQL
通過索引查數據時,索引的葉子節點上已經包含要查詢的數據,這樣就不需要回表查詢了。這就是著名的覆蓋索引。MySQL
的InnoDB
引擎只有B-Tree
類型的索引具有覆蓋索引,哈希索引、空間索引和全文索引都沒有覆蓋索引。
覆蓋索引:查詢只需要訪問索引,而無須訪問數據行
覆蓋索引的原理:索引的葉子節點上包含該行的所有索引列
例如id、name、state、address
四列,其中id
爲主鍵,(name,state,address)
是聯合索引# 對於下面三個sql,explain查看執行計劃,發現Extra列的值爲Using where; Using index,對應上面Extra的類型 select name , address from table_name where name = 'p7'; select id , name from table_name where name = 'p7'; select state from table_name where name = 'p7'; # 下面這個sql的Extra列的值是Using where,因爲查詢的password不是索引列也不是索引列的一部分 select id , name , password from table_name where name = 'p7';
-
like
尾部模糊查詢索引有效,頭部模糊查詢索引失效,利用覆蓋索引優化like
頭部模糊查詢# 通過覆蓋索引查詢id select id from table_name where name like '%p7%'; # 通過id查詢數據 select * from table_name where id = xxx;
-
in
索引有效,not in
索引無效 -
or
前的條件中使用索引,後面條件沒有使用索引,那麼查詢不使用索引 -
MySQL
執行SQL
時發現全表掃描比索引快,則不使用索引。
示例1:name
是單列索引,非主鍵,數據庫中99條數據的name
都是盲僧,只有一條數據是p7
,在執行where name = '盲僧'
時,MySQL
會放棄索引,進行全表掃描。
示例2:name
是單列索引,非主鍵,如果數據庫中所有name
字段沒有null
的情況,is null
使用索引,is not null
進行全表掃描;如果name
爲null
佔大多數,is null
進行全表掃描,is not null
使用索引
上面兩種情況,均是MySQL
內部優化,根據數據庫中的數據佔比而定的