你的like語句爲啥沒索引?

本文旨在用最通俗的語言講述最枯燥的基本知識

這個話題比較有意思。
昨天中午吃完飯間忽然有個同事蹦出了一句:「like有索引嗎?」,我順口就說沒有,另外一個同事反駁說有啊,還有些同事說看狀況的有,這下有點懵逼了,都不知道那種說法是正確的,因而決定花了個半小時來研究驗證這個問題,終於獲得答案。sql

怎麼驗證的呢?編程

坊間有傳言:MySQL性能優化有個神器,叫作explain,它能夠對select語句進行分析而且輸出詳細的select執行過程的詳細信息,讓開發者從這些信息中得到優化的思路。

下面來說講這個MySQL提供的explain命令:緩存

語法:explain SQL語句
例如:性能優化

explain select * from user where id=1

執行完畢以後,它的輸出有如下字段:函數

  • id
  • select_type
  • table
  • partitions
  • type
  • possible_keys
  • key
  • key_len
  • ref
  • rows
  • Extra

要想知道explain命名怎麼使用,就必須把這些字段搞清楚性能

1. id

SELECT查詢的標識符, 每一個SELECT語句都會自動分配一個惟一的標識符優化

2. select_type

每一個select查詢字句的類型,具體類型以及對應做用以下表:spa

類型名 解釋
SIMPLE 簡單SELECT,不使用UNION或子查詢等
PRIMARY 查詢中若包含任何複雜的子部分,最外層的select被標記爲PRIMARY
UNION UNION中的第二個或後面的SELECT語句
DEPENDENT UNION UNION中的第二個或後面的SELECT語句,取決於外面的查詢
UNION RESULT UNION的結果
SUBQUERY 子查詢中的第一個SELECT
DEPENDENT SUBQUERY 子查詢中的第一個SELECT,取決於外面的查詢
DERIVED 派生表的SELECT, FROM子句的子查詢
UNCACHEABLE SUBQUERY 一個子查詢的結果不能被緩存,必須從新評估外連接的第一行

3. table

顯示這一行的數據是查哪張表的,不過有時短路顯示的不是真實的表名。3d

4. partitions

匹配的分區(這個目前用處不大)code

5. type

訪問類型,表示MySQL在表中找到所需行的方式,對應的值和解釋以下:

類型名 優級別 解釋
system 1 表僅有一行
const 2 表最多有一個匹配行,在查詢開始時即被讀取
eq_ref 3 使用primary key或者unique key做爲多表鏈接的條件,僅從該表中讀取一行
ref 4 做爲查詢條件的索引在每一個表匹配索引值的行從表中讀取出來
fulltext 5 全文索引檢索
ref_or_null 6 和ref一致,但增長了NULL值查詢支持
index_merge 7 表示使用了索引合併優化方法
unique_subquery 8 使用了替換了in子查詢
index_subquery 9 使用了替換了in子查詢,但只適用於子查詢中的非惟一索引
range 10 只檢索給定範圍的行,使用一個索引來選擇行
index 11 全表掃描,但掃描表的方式是按索引的次序進行
ALL 12 全表掃描的方式找到匹配的行

type做爲訪問類型,其值表明着當前查詢所用的類型,是體現性能的一個重要指標,從表中能夠看到,從上到下,掃描表的方式愈來愈寬,性能也就愈來愈差,所以,對於一個查詢,最好能保持在range級別以上。

6. possible_keys

主動指出查詢能用哪一個索引在表中找到記錄
也就是會列出在查詢中的字段中有索引的字段,但不必定被查詢所用。

7. key

顯示再查詢中實際使用的索引/鍵,若是沒有索引,則顯示NULL。
但若是想強制查詢中使用或忽視possible_keys列中的索引,則能夠在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

8. key_len

表示索引中使用的字節數。

9. ref

表示哪些列或常量被用於查找索引列上的值。

10. rows

顯示當前查詢估算到的查找到匹配記錄所需的記錄行數。

11. Extra

顯示當前查詢所用的解決方式,它有如下幾種狀況:

類型名 解釋
Using where 列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,
Using temporary 表示MySQL須要使用臨時表來存儲結果集,常見於排序和分組查詢
Using filesort MySQL中沒法利用索引完成的排序操做稱爲「文件排序」
Using join buffer 改值強調了在獲取鏈接條件時沒有使用索引,而且須要鏈接緩衝區來存儲中間結果。若是出現了這個值,那應該注意,根據查詢的具體狀況可能須要添加索引來改進能。
Impossible where 這個值強調了where語句會致使沒有符合條件的行。
Select tables optimized away 這個值意味着僅經過使用索引,優化器可能僅從聚合函數結果中返回一行

講完了語法,咱們來實際操做一波,首先建立個表:

-- 建立表
CREATE TABLE test(
id INT(11) NOT NULL AUTO_INCREMENT,
uname VARCHAR(255),
PRIMARY KEY(id) 
);

而後給uname字段加上索引:

-- 添加索引
ALTER TABLE test ADD INDEX uname_index (uname);

查看一下索引是否添加成功:

-- 查看是否有索引
SHOW INDEX FROM test;

輸出結果爲:

能夠看出索引已經建立成功,接下來添加一些數據:

-- 添加一些數據
INSERT INTO test VALUES(1,'jay');
INSERT INTO test VALUES(2,'ja');
INSERT INTO test VALUES(3,'bril');
INSERT INTO test VALUES(4,'aybar');

一切準備就緒,下面用explain這個命令來探究一些like語句是否有索引,
like有四種狀況,分別爲沒有%、 %% 、左%、右%、

1. like 字段名

EXPLAIN SELECT * FROM test WHERE uname LIKE 'j';

輸出爲:

能夠看出:
type的值爲:range,key的值爲uname_index,也就是說這種狀況下,使用了索引。

2. like %字段名%

EXPLAIN SELECT * FROM test WHERE uname LIKE '%j%';

輸出爲:

能夠看出:
type的值爲ALL也就是全表掃描,並且key的值爲NULL,也就是說沒用到任何索引。

3. like %字段名

EXPLAIN SELECT * FROM test WHERE uname LIKE '%j';

輸出爲:

能夠看出:
type的值爲ALL,key的值爲NULL,一樣沒用到索引。

4. like 字段名%

EXPLAIN SELECT * FROM test WHERE uname LIKE 'j%';

輸出爲:

能夠看出:
type的值爲:range,key的值爲uname_index,也就是說這種狀況下,使用了索引。

總結

由上面的試驗能夠總結出當前SQL中like是否使用索引的規律:
要是當前SQL中like語句要使索引生效,like後不能以%開始,也就是說 :

1. (like %字段名%)、(like %字段名)這類語句會使索引失效,
2. (like 字段名)、(like 字段名%)這類語句索引是能夠正常使用。

其它

爲了查證like索引的問題,研究了MySQL神奇explain,但explain不只僅只能檢查索引使用狀況,還能夠提供不少其它的性能優化方面的幫助,至於具體的使用,其實跟上面講的同樣,把explain結果列出來,而後順藤摸瓜查閱相關的字段就能夠獲得相應的內容。


以爲本文對你有幫助?請分享給更多人
關注「編程無界」,提高裝逼技能

相關文章
相關標籤/搜索