干貨:你不知道的mysql多列索引的建立和優化

對于單列索引,沒有太多的話題,但是對于多列索引的建立,一個好的多列索引能使用的場景應可以涵蓋很多,減少其他不必要的索引空間,就有很多事情需要注意。


0.首先來了解索引的物理結構:

http://www.lxweimin.com/p/1775b4ff123a


1.where 子句中的多列索引

如果表有多個列索引,可以使用任何左邊的前綴索引的優化器來查找行。如何通過前綴索引來查找行我們通過如下例子來詳細了解:

現有表formatting,數據如下圖所示:


formatting

在該表上建立多列索引:

create index idx_custid_qty_empid on formatting(custid,qty,empid)

1.1可以完全使用該索引的優化的情況

例如,如果你有一個三列的索引(col1、col2 col3),有索引搜索功能(col1),(col1,col2),(col1、col2、col3)。

1)where中條件只有col1等于常量;
說明:表中custid為A的數據共4行,查詢中索引優化得到的等于結果數據。

mysql> explain select orderid,orderdate from formatting where custid='A'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: const rows: 4 filtered: 100.00 Extra: Using index

2)where中條件只有col1為范圍(>,<,>=,<=);
說明:表中custid大于A的有5行,索引起到完全優化的作用。

`mysql> explain select orderid,orderdate from formatting where custid>'A'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid

      key: idx_custid_qty_empid
  key_len: 32
      ref: NULL
     rows: 5
 filtered: 100.00
    Extra: Using where; Using index`

3)where中條件有col1,col2,且col1等于常量,col2等于常量;
說明:表中custid為A,qty等于20的數據只有一條,索引完全優化查詢。

mysql> explain select orderid,orderdate from formatting where custid='A' and qty=20\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 36 ref: const,const rows: 1 filtered: 100.00 Extra: Using index

4)where中條件有col1,col2,且col1等于常量,col2等于范圍;
說明:表中custid為A,qty大于等于20的數據有2條,索引完全優化查詢。

mysql> explain select orderid,orderdate from formatting where custid='A' and qty>=20\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: range possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 36 ref: NULL rows: 2 filtered: 100.00 Extra: Using where; Using index

5)where中條件有col1,col2,col3,且col1,col2,col3均等于常量;
說明:表中custid為A,qty等于10,empid等于2的數據有1條,索引完全優化查詢。

mysql> explain select orderid,orderdate from formatting where custid='A' and qty=10 and empid=2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 40 ref: const,const,const rows: 1 filtered: 100.00 Extra: Using index

6)where中條件有col1,col2,col3,且col1,col2均等于常量,col3為范圍;
說明:表中custid為A,qty等于10,empid大于2的數據有1條,索引完全優化查詢。

mysql> explain select orderid,orderdate from formatting where custid='A' and qty=10 and empid>2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: range possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 40 ref: NULL rows: 1 filtered: 100.00 Extra: Using where; Using index

1.2可以部分使用該索引的優化的情況

1)where中條件col1等于常量,無col2,col3的常量或范圍,只能用到col1的索引;
說明:custid='A' and empid=2的數據僅有一條,但由于empid在多列索引中為第三列,故僅能使用多列索引優化custid='A'的4條數據。

mysql> explain select orderid,orderdate from formatting where custid='A' and empid=2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: const rows: 4 filtered: 11.11 Extra: Using where; Using index

2)where中條件col1等于常量,col2的范圍,col3的常量或范圍,只能用到col1、col2的索引;
說明:custid='A' and qty<20 and empid=2的數據僅有一條,但由于qty為范圍,故僅能使用多列索引優化custid='A' and qty<20的2條數據。

mysql> explain select orderid,orderdate from formatting where custid='A' and qty<20 and empid=2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: range possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 36 ref: NULL rows: 2 filtered: 11.11 Extra: Using where; Using index

3)where中條件col1的范圍,col2、col3的常量或范圍,只能用到col1的索引;
說明:custid>'A' and empid=2的數據僅有一條,但由于custid為范圍查詢,故僅能使用多列索引優化custid>'A'的5條數據。

mysql> explain select orderid,orderdate from formatting where custid>'A' and empid=2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: range possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: NULL rows: 5 filtered: 11.11 Extra: Using where; Using index

1.2不能使用該索引的優化的情況

下列情況均需要掃描所有行再進行篩選,因為多列索引的第一列沒有在條件中。
1)col2 常量或范圍;
2)col3 常量或范圍;
3)col2、col3 常量或范圍;
說明:由于沒有custid的條件需要掃描9行數據。為何下面的例子中,卻使用了type: index? 因為:當索引是覆蓋索引并且可以用來滿足需要從表中得到的所有數據時,只有索引樹被掃描。在這種情況下,(EXPLAIN命令)返回記錄里的Extra字段提示Using index。此表中的orderid,orderdate為聯合主鍵。

mysql> explain select orderid,orderdate from formatting where qty=20\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: index possible_keys: NULL key: idx_custid_qty_empid key_len: 40 ref: NULL rows: 9 filtered: 11.11 Extra: Using where; Using index

我們添加一列新的列名叫version,并對其賦值,如下圖所示。

newtable

再來看一下上面會發生什么變化,此時要查詢多列避免出現覆蓋索引,我們發現沒有使用任何索引了:

mysql> desc select orderid,orderdate,empid,custid,qty,version from formatting where qty=20\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 10 filtered: 10.00 Extra: Using where

2.order by 子句中的多列索引

2.1.在不是所有的order by子句中的字段完全滿足索引,在一些情況下依然可以使用索引。

1) order by子句中的字段順序與索引字段順序一致:

create index idx_custid_qty on formatting(custid,qty);
mysql> desc select orderid from formatting order by custid,qty\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: index possible_keys: NULL key: idx_custid_qty key_len: 36 ref: NULL rows: 10 filtered: 100.00 Extra: Using index

注意:此時若使用 select * 則不會使用索引。

`mysql> desc select * from formatting order by custid,qty\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 9
filtered: 100.00

    Extra: Using filesort`

那么為何有索引卻不能使用呢,我們結合最初的鏈接中的索引物理結構來理解:
第一種方法是直接讀取表記錄,然后根據order by 列去排序。
第二種方法是先掃描order by 上的索引,由于索引本身就是排好序的,因此根據索引的順序再讀出表的記錄即可,省去排序的過程。
我們可以看出第一種方法直接讀取表記錄就可以了.第二種方法需要先讀取索引,然后根據索引去讀取表記錄,
io會變得很隨機,因此這種方法會成本比較高,所以優化器會選擇第一種執行執行劃。

2)按照索引順序的where和order by子句依舊可以使用,但where條件中必須使用字段名> < =某常量。

mysql> desc select * from formatting where custid='A' order by qty\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty key: idx_custid_qty key_len: 32 ref: const rows: 4 filtered: 100.00 Extra: Using index condition

2.2.但是有些情況是不能使用索引的

1)同一個order by 中的字段分別有多個索引。
建立custid和empid的單獨索引。

create index idx_custid on formatting(custid) create index idx_empid on formatting(empid) mysql> desc select orderdate from formatting order by custid,empid\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 10 filtered: 100.00 Extra: Using filesort

2)where 與order by 字段的順序與索引順序不同,排序不能使用索引。例子中沒有使用聯合索引,只能在where使用單個索引。

`create index idx_custid_qty_empid on formatting(custid,qty,empid)
create index idx_qty on formatting(qty)
mysql> desc select orderdate from formatting where qty=10 order by custid,empid\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_qty
key: idx_qty

  key_len: 4
      ref: const
     rows: 2
 filtered: 100.00
    Extra: Using index condition; Using filesort`

3) 同時包含asc與desc,排序不能使用索引,只能在where條件使用索引,排序Using filesort。

刪除其他索引 create index idx_custid_qty_empid on formatting(custid,qty,empid) mysql> explain select orderid,orderdate,empid,custid,qty,version from formatting where custid='A' order by qty desc ,empid asc\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: const rows: 4 filtered: 100.00 Extra: Using index condition; Using filesort

4) order by 字段中有計算,排序不能使用索引。
先來看不使用計算時,可以使用索引的情況:

mysql> explain select orderid,orderdate,empid,custid,qty,version from formatting where custid='A' order by qty\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: const rows: 4 filtered: 100.00 Extra: Using index condition

增加計算后,排序不能使用索引,需要Using filesort

mysql> explain select orderid,orderdate,empid,custid,qty,version from formatting where custid='A' order by qty+1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: ref possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 32 ref: const rows: 4 filtered: 100.00 Extra: Using index condition; Using filesort

5)ORDER BY 和 GROUP BY的表達式不同,排序不能使用索引。
先來看下ORDER BY 和 GROUP BY的表達式相同情況,是不需要Using filesort。

`mysql> explain select custid,qty from formatting group by custid,qty order by custid,qty\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: index

possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 40
ref: NULL
rows: 10
filtered: 100.00
Extra: Using index`

但是,如果不同的情況排序是不能使用索引的,需要再一次Using filesort。

mysql> explain select custid,qty from formatting group by custid,qty order by qty\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: formatting partitions: NULL type: index possible_keys: idx_custid_qty_empid key: idx_custid_qty_empid key_len: 40 ref: NULL rows: 10 filtered: 100.00 Extra: Using index; Using temporary; Using filesort

6) join的表ORDER BY字段來自不同的表只能使用主表的索引。
如果查詢需要關聯多張表,則只有當ORDER BY子句引用的字段全部為第一個表時,才能使用索引做排序。此時需要注意,在inner join時,優化器將哪張表選做主表,就只能使用此表的索引。
其中一定要滿足order by 在單表查詢中的要求,若去掉T1.empid=2,則同2.1中1情況不能使用排序索引。
使用主表的排序:

mysql> desc select * from formatting t1 left join emp t2 on t1.empid=t2.empid WHERE T1.empid=2 order by T1.custid \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ref possible_keys: idx_empid_custid key: idx_empid_custid key_len: 4 ref: const rows: 2 filtered: 100.00 Extra: Using index condition *************************** 2. row *************************** id: 1 select_type: SIMPLE table: t2 partitions: NULL type: ref possible_keys: idx_empid key: idx_empid key_len: 5 ref: const rows: 1 filtered: 100.00 Extra: NULL

使用第二張表排序,需要Using filesort:

mysql> desc select * from formatting t1 left join emp t2 on t1.empid=t2.empid WHERE T1.empid=2 order by T2.empname \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ref possible_keys: idx_empid_custid key: idx_empid_custid key_len: 4 ref: const rows: 2 filtered: 100.00 Extra: Using temporary; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: t2 partitions: NULL type: ref possible_keys: idx_empid key: idx_empid key_len: 5 ref: const rows: 1 filtered: 100.00 Extra: NULL

對于上述去掉where條件情況可以如下優化語句:
優化前語句:

SELECT * FROM a LEFT JOIN b ON a.id=b.a_id ORDER a.id DESC

優化后語句:

SELECT * FROM a LEFT JOIN b ON a.id=b.a_id JOIN (SELECT id FROM a ORDER BY id DESC) a_order ON a.id = a_order.id

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容