MySQL 索引分析與優化

Thresh

EXPLAIN

MySQL 提供了一個 EXPLAIN 命令,它能夠對 SELECT 語句進行分析,並輸出 SELECT 執行的詳細信息,供開發人員有針對性的優化。算法

EXPLAIN SELECT * from USERS where id >1 ;

image.png

select_type
表示查詢的類型。經常使用的值以下:緩存

SIMPLE : 表示查詢語句不包含子查詢或union
PRIMARY:表示此查詢是最外層的查詢
UNION:表示此查詢是UNION的第二個或後續的查詢
EXPLAIN SELECT * from user WHERE id < 3;
DEPENDENT UNION:UNION中的第二個或後續的查詢語句,使用了外面查詢結果
UNION RESULT:UNION的結果
SUBQUERY:SELECT子查詢語句
DEPENDENT SUBQUERY:SELECT子查詢語句依賴外層查詢的結果。

type
表示存儲引擎查詢數據時採用的方式。比較重要的一個屬性,經過它能夠判斷出查詢是全表掃描仍是基於索引的部分掃描。經常使用屬性值以下,從上至下效率依次加強。函數

ALL:表示全表掃描,性能最差。
index:表示基於索引的全表掃描,先掃描索引再掃描全表數據。
range:表示使用索引範圍查詢。使用>、>=、<、<=、in等等。
ref:表示使用非惟一索引進行單值查詢。
eq_ref:通常狀況下出如今多表join查詢,表示前面表的每個記錄,都只能匹配後面表的一行結果。
const:表示使用主鍵或惟一索引作等值查詢,常量查詢。
NULL:表示不用訪問表,速度最快

possible_keys
表示查詢時可以使用到的索引。注意並不必定會真正使用,顯示的是索引名稱。性能

key
表示查詢時真正使用到的索引,顯示的是索引名稱。優化

rows
MySQL查詢優化器會根據統計信息,估算SQL要查詢到結果須要掃描多少行記錄。原則上rows是越少效率越高,能夠直觀的瞭解到SQL效率高低。spa

key_len
表示查詢使用了索引的字節數量。能夠判斷是否所有使用了組合索引。
key_len的計算規則以下:code

字符串類型
    字符串長度跟字符集有關:latin1=一、gbk=二、utf8=三、utf8mb4=4
    char(n):n*字符集長度
    varchar(n):n * 字符集長度 + 2字節
    
數值類型
    TINYINT:1個字節
    SMALLINT:2個字節
    MEDIUMINT:3個字節
    INT、FLOAT:4個字節
    BIGINT、DOUBLE:8個字節
    
時間類型
    DATE:3個字節
    TIMESTAMP:4個字節
    DATETIME:8個字節
    
字段屬性
    NULL屬性佔用1個字節,若是一個字段設置了NOT NULL,則沒有此項

Extra
Extra表示不少額外的信息,各類操做會在Extra提示相關信息,常見幾種以下:blog

Using where
    表示查詢須要經過索引回表查詢數據。

Using index
    表示查詢須要經過索引,索引就能夠知足所需數據。

Using filesort
    表示查詢出來的結果須要額外排序,數據量小在內存,大的話在磁盤,所以有Using filesort建議優化。

Using temprorary
    查詢使用到了臨時表,通常出現於去重、分組等操做。

回表查詢

InnoDB索引有聚簇索引(主鍵索引)和輔助索引。聚簇索引的葉子節點存儲行記錄,InnoDB必需要有,且只有一個。輔助索引的葉子節點存儲的是主鍵值和索引字段值,經過輔助索引沒法直接定位行記錄,一般狀況下,須要掃碼兩遍索引樹。
先經過輔助索引定位主鍵值,而後再經過聚簇索引定位行記錄,這就叫作回表查詢,它的性能比掃一遍索引樹低。排序

總結:經過索引查詢主鍵值,而後再去聚簇索引查詢記錄信息索引

覆蓋索引

即explain的輸出結果Extra字段爲Using index時,可以觸發索引覆蓋。

EXPLAIN SELECT e.* from EMPLOYEE e INNER JOIN COMPANY c on e.companyId = c.id

最左前綴原則

複合索引使用時遵循最左前綴原則,最左前綴顧名思義,就是最左優先,即查詢中使用到最左邊的列,那麼查詢就會使用到索引,若是從索引的第二列開始查找,索引將失效

CREATE TABLE `user2` (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL DEFAULT '',
  `password` varchar(20) NOT NULL DEFAULT '',
  `usertype` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`userid`),
  KEY `a_b_c_index` (`username`,`password`,`usertype`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

當存在username時會使用索引查詢:

explain select * from user2 where username = '1' and password = '1';

當沒有username時,不會使用索引查詢:

explain select * from user2 where password = '1';

LIKE查詢

MySQL在使用like模糊查詢時,索引能不能起做用?

MySQL在使用Like模糊查詢時,索引是能夠被使用的,只有把%字符寫在後面纔會使用到索引。

select * from user where name like '%o%'; //不起做用
select * from user where name like 'o%'; //起做用
select * from user where name like '%o'; //不起做用

NULL查詢

若是MySQL表的某一列含有NULL值,那麼包含該列的索引是否有效?

對MySQL來講,NULL是一個特殊的值,從概念上講,NULL意味着「一個未知值」,它的處理方式與其餘值有些不一樣。
好比:不能使用=,<,>這樣的運算符,對NULL作算術運算的結果都是NULL,count時不會包括NULL行等,NULL比空字符串須要更多的存儲空間等

NULL列須要增長額外空間來記錄其值是否爲NULL。對於MyISAM表,每個空列額外佔用一位,四捨五入到最接近的字節。

雖然MySQL能夠在含有NULL的列上使用索引,但NULL和其餘數據仍是有區別的,不建議列上容許爲NULL。最好設置NOT NULL,並給一個默認值,好比0和 ‘’ 空字符串等,若是是datetime類型,也能夠設置系統當前時間或某個固定的特殊值,例如'1970-01-01 00:00:00'。

索引與排序

MySQL查詢支持filesort和index兩種方式的排序,filesort是先把結果查出,而後在緩存或磁盤進行排序操做,效率較低。使用index是指利用索引自動實現排序,不需另作排序操做,效率會比較高。

filesort有兩種排序算法:雙路排序和單路排序。

  • 雙路排序:須要兩次磁盤掃描讀取,最終獲得用戶數據。第一次將排序字段讀取出來,而後排序;第二次去讀取其餘字段數據。
  • 單路排序:從磁盤查詢所需的全部列數據,而後在內存排序將結果返回。若是查詢數據超出緩存sort_buffer,會致使屢次磁盤讀取操做,並建立臨時表,最後產生了屢次IO,反而會增長負擔。
  • 解決方案:少使用select *;增長sort_buffer_size容量max_length_for_sort_data容量。

若是咱們Explain分析SQL,結果中Extra屬性顯示Using filesort,表示使用了filesort排序方式,須要優化。若是Extra屬性顯示Using index時,表示覆蓋索引,也表示全部操做在索引上完成,也可使用index排序方式,建議你們儘量採用覆蓋索引。

會使用index方式的排序

ORDER BY 子句索引列組合知足索引最左前列
    explain select id from user order by id; //對應(id)、(id,name)索引有效

WHERE子句+ORDER BY子句索引列組合知足索引最左前列
    explain select id from user where age=18 order by name; //對應 (age,name)索引

會使用filesort方式的排序

對索引列同時使用了ASC和DESC
    explain select id from user order by age asc,name desc; //對應 (age,name)索引

WHERE子句和ORDER BY子句知足最左前綴,但where子句使用了範圍查詢(例如>、<、in等)
    explain select id from user where age>10 order by name; //對應 (age,name)索引

ORDER BY或者WHERE+ORDER BY索引列沒有知足索引最左前列
    explain select id from user order by name; //對應(age,name)索引
    
使用了不一樣的索引,MySQL每次只採用一個索引,ORDER BY涉及了兩個索引
    explain select id from user order by name,age; //對應(name)、(age)兩個索引

WHERE子句與ORDER BY子句,使用了不一樣的索引
    explain select id from user where name='tom' order by age; //對應 (name)、(age)索引

WHERE子句或者ORDER BY子句中索引列使用了表達式,包括函數表達式
    explain select id from user order by abs(age); //對應(age)索引
相關文章
相關標籤/搜索