由淺入深探究mysql索引結構原理、性能分析與優化(二)

(1.1)能正確的利用索引mysql

l  Where子句表達式順序是(username)web

  1. mysql> explain select * from one where username='abgvwfnt';  sql

  2. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  性能

  3. | id | select_type | table | type | possible_keys | key      | key_len | ref   |rows | Extra       |  測試

  4. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  優化

  5. |  1 | SIMPLE      | one   | ref  | username      | username | 24      | const |5 | Using where |  編碼

  6. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  orm

  7. 1 row in set (0.00 sec)  索引

l  Where子句表達式順序是(username,password)it

  1. mysql> explain select * from one where username='abgvwfnt' and password='123456';  

  2. +----+-------------+-------+------+---------------+----------+---------+-------------+------+-------------+  

  3. | id | select_type | table | type | possible_keys | key      | key_len | ref | rows | Extra       |  

  4. +----+-------------+-------+------+---------------+----------+---------+-------------+------+-------------+  

  5. |  1 | SIMPLE      | one   | ref  | username      | username | 43      | const,const |    1 | Using where |  

  6. +----+-------------+-------+------+---------------+----------+---------+-------------+------+-------------+  

  7. 1 row in set (0.00 sec)  

l  Where子句表達式順序是(username,password, last_login)

  1. mysql> explain select * from one where username='abgvwfnt' and password='123456'and last_login='1338251170';  

  2. +----+-------------+-------+------+---------------+----------+---------+-------------------+------+-------------+  

  3. | id | select_type | table | type | possible_keys | key      | key_len | ref| rows | Extra       |  

  4. +----+-------------+-------+------+---------------+----------+---------+-------------------+------+-------------+  

  5. |  1 | SIMPLE   | one   | ref  | username     | username | 83      | const,const,const |    1 | Using where |  

  6. +----+-------------+-------+------+---------------+----------+---------+-------------------+------+-------------+  

  7. 1 row in set (0.00 sec)  

上面能夠看出type=ref 是多列索引,key_len分別是2四、4三、83,這說明用到的索引分別是(username), (username,password), (username,password, last_login );row分別是五、一、1檢索的數據行都不多,由於這三個查詢都按照索引前綴原則,能夠利用到索引。

 

(1.2)不能正確的利用索引

l  Where子句表達式順序是(password, last_login)

  1. mysql> explain select * from one where password='123456'and last_login='1338251170';  

  2. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows| Extra       |  

  4. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  5. |  1 | SIMPLE      | one   | ALL  | NULL          | NULL | NULL    | NULL | 20146 | Using where |  

  6. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  7. 1 row in set (0.00 sec)  

l  Where 子句表達式順序是(last_login)

  1. mysql> explain select * from one where last_login='1338252525';  

  2. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows| Extra       |  

  4. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  5. |  1 | SIMPLE      | one   | ALL  | NULL          | NULL | NULL    | NULL | 20146 | Using where |  

  6. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  7. 1 row in set (0.00 sec)  

以上的兩條語句都不是以username開始,這樣是用不了索引,經過type=all(全表掃描),key_len=null,rows都很大20146

Ps:one表裏只有20003條數據,爲何出現20146,這是優化器對錶的一個估算值,不精確的。

l  Where 子句表達式雖然順序是(username,password, last_login)或(username,password)但第一個是有範圍’<’、’>’,’<=’,’>=’等出現

  1. mysql> explain select * from one where username>'abgvwfnt' and password ='123456'and last_login='1338251170';  

  2. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows| Extra       |  

  4. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  5. |  1 | SIMPLE      | one   | ALL  | username      | NULL | NULL    | NULL | 20146 | Using where |  

  6. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  7. 1 row in set (0.00 sec)  

這個查詢很明顯是遍歷全部表,一個索引都沒用到,非第一列出現範圍(password列或last_login列),則能利用索引到首先出現範圍的一列,也就是「where username='abgvwfnt' and password >'123456'and last_login='1338251170';」或則「where username='abgvwfnt' and password >'123456'and last_login<'1338251170';」索引長度ref_len=43,索引檢索到password列,因此考慮多列索引的時候把那些查詢語句用的比較的列放在最後(或非第一位)。

l  斷層,便是where順序(username, last_login)

  1. mysql> explain select * from one where username='abgvwfnt' and last_login='1338252525';  

  2. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  

  3. | id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra       |  

  4. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  

  5. |  1 | SIMPLE   | one   | ref  | username   | username | 24     | const |5 | Using where |  

  6. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+  

  7. 1 row in set (0.00 sec)  

注意這裏的key_len=24=8*3(8是username的長度,3是utf8編碼),rows=5,和下面一條sql語句搜索出來同樣

  1. mysql>  select * from one where username='abgvwfnt';  

  2. +-------+----------+----------+-------+------------+  

  3. | id    | username | password | level | last_login |  

  4. +-------+----------+----------+-------+------------+  

  5. |  3597 | abgvwfnt | 234567   |     0 | 1338251420 |  

  6. |  7693 | abgvwfnt | 456789   |     0 | 1338251717 |  

  7. | 11789 | abgvwfnt | 456789   |     0 | 1338251992 |  

  8. | 15885 | abgvwfnt | 456789   |     0 | 1338252258 |  

  9. | 19981 | abgvwfnt | 456789   |     0 | 1338252525 |  

  10. +-------+----------+----------+-------+------------+  

  11. 5 rows in set (0.00 sec)  

  12.  

  13. mysql>  select * from one where username='abgvwfnt' and last_login='1338252525';  

  14. +-------+----------+----------+-------+------------+  

  15. | id    | username | password | level | last_login |  

  16. +-------+----------+----------+-------+------------+  

  17. | 19981 | abgvwfnt | 456789   |     0 | 1338252525 |  

  18. +-------+----------+----------+-------+------------+  

  19. 1 row in set (0.00 sec)  

這個就是要的返回結果,因此能夠知道斷層(username,last_login),這樣只用到username索引,把用到索引的數據再從新檢查last_login條件,這個相對全表查詢來講仍是有性能上優化,這也是不少sql優化文章中提到的where 範圍查詢要放在最後(這不絕對,但能夠利用一部分索引)

(1.3)若是一個查詢where子句中確實不須要password列,那就用「補洞」。

  1. mysql> select distinct(password) from one;  

  2. +----------+  

  3. | password |  

  4. +----------+  

  5. | 234567   |  

  6. | 345678   |  

  7. | 456789   |  

  8. | 123456   |  

  9. +----------+  

  10. 4 rows in set (0.08 sec)

能夠看出password列中只有這幾個值,固然在現實中不可能密碼有這麼多同樣的,再說數據也可能不斷更新,這裏只是舉例說明補洞的方法

  1. mysql> explain select * from one where username='abgvwfnt' and password in('123456','234567','345678','456789')

  2. and last_login='1338251170';  

  3. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  4. | id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |  

  5. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  6. |  1 | SIMPLE    | one | range | username    | username| 83      | NULL |4 | Using where |  

  7. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  8. 1 row in set (0.00 sec)  

能夠看出ref=83 全部的索引都用到了,type=range是由於用了in子句。

這個被「補洞」列中的值應該是有限的,可預知的,如性別,其值只有男和女(加多一個不男不女也無妨)。

「補洞」方法也有瓶頸,當不少列,且須要補洞的相應列(能夠多列)的值雖有限但不少(如中國城市)的時候,優化器在優化時組合起來的數量是很大,這樣的話就要作好基準測試和性能分析,權衡得失,取得一個合理的優化方法。

(1.4)like

  1. mysql> explain select * from one where username like 'abgvwfnt%';  

  2. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  3. | id | select_type | table | type  | possible_keys | key      | key_len | ref  |  

  4. rows | Extra       |  

  5. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  6. |  1 | SIMPLE      | one   | range | username      | username | 24      | NULL |  

  7. 5 | Using where |  

  8. +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+  

  9. 1 row in set (0.00 sec)  

  10. mysql> explain select * from one where username like '%abgvwfnt%';  

  11. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  12. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows| Extra       |  

  13. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  14. |  1 | SIMPLE      | one   | ALL  | NULL          | NULL | NULL    | NULL | 20259 | Using where |  

  15. +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+  

  16. 1 row in set (0.01 sec)  

對比就知道like操做abgvwfnt%能用到索引,%abgvwfnt%用不到

---------------------------------------------------------------------------------------------

相關文章
相關標籤/搜索