InnoDB引擎之索引的優化及有效使用

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時必須要關注的。

  • typemysql決定如何查找表中的行,下面是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:使用索引分組
    

索引的使用和優化

  1. 是否使用索引與where後的條件順序無關

  2. 索引列使用比較符號,會使用索引,單列索引和聯合索引都可以,但是索引後面的索引不會被使用,聯合索引比較詳見第5條的案例

  3. 儘量使用聯合索引,減少使用單列索引。例如列name、state、address
    聯合索引(name,state,address)相當於創建了三個索引:name、name+state、name+state+address
    單列索引name、state、address,當三個索引一起作爲查詢條件時,MySQL會選擇一個最優的索引(數據的辨識度高的索引)來使用,並不會使用全部索引。
    辨識度高是指根據索引查出的數據量小,例如數據庫中有100w條數據,namestate都是單列索引,其中name='瞎子' and state = 0的數據只有一條,其他數據均爲name='瞎子' and state = 1,對於select * from table_name where name = '瞎子' and state = 0;MySQL會使用索引status

  4. 索引全值匹配,例如聯合索引(name,state,address)SQL:select * from table_name where state = '1' and name = '小喬' and address = '北京';

  5. 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 = ''
    
  6. 通常情況下,我們都會根據where條件來創建合適的索引,這是索引優化的一個方面,但數據庫通過索引查詢數據後要不要回表查詢,這也是索引優化的方面,也就是說MySQL通過索引查數據時,索引的葉子節點上已經包含要查詢的數據,這樣就不需要回表查詢了。這就是著名的覆蓋索引MySQLInnoDB引擎只有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';
    
  7. like尾部模糊查詢索引有效,頭部模糊查詢索引失效,利用覆蓋索引優化like頭部模糊查詢

    # 通過覆蓋索引查詢id
    select id from table_name where name like '%p7%';
    # 通過id查詢數據
    select * from table_name where id = xxx;	
    
  8. in索引有效,not in索引無效

  9. or前的條件中使用索引,後面條件沒有使用索引,那麼查詢不使用索引

  10. MySQL執行SQL時發現全表掃描比索引快,則不使用索引。
    示例1:name是單列索引,非主鍵,數據庫中99條數據的name都是盲僧,只有一條數據是p7,在執行where name = '盲僧'時,MySQL會放棄索引,進行全表掃描。
    示例2:name是單列索引,非主鍵,如果數據庫中所有name字段沒有null的情況,is null使用索引,is not null進行全表掃描;如果namenull佔大多數,is null進行全表掃描,is not null使用索引
    上面兩種情況,均是MySQL內部優化,根據數據庫中的數據佔比而定的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章