Index Merge特性算法
在MySQL 5.5以前版本中,查詢或子查詢被限制在一個表只能使用一個索引(回表查詢除外)。性能
假設表TB1001上C1和C2列分別有單列索引,如對下面查詢:測試
SELECT * FROM TB1001 WHERE C1='XXX' OR C2='XXX';
單獨使用任一索引都沒法獲取到全部知足條件的數據,所以查詢只能使用全表掃描。
在MySQL 5.5版本中引入Index Merge特性,容許:
查詢對一個表上多個索引進行範圍掃描並將多個掃描結果進行合併(UNION/INTERSECT)。
Index Merge三種合併算法:優化
1、Index Merge Intersect:對多個結果集求交集 2、Index Merge Union:對多個結果集求UNION集合(無需對結果集排序) 3、Index Merge Sort-Union:對多個結果集先排序再求UNION集合
Index Merge Intersect算法spa
當查詢過濾條件(WHERE部分)上使用AND關聯多個不一樣KEY的過濾條件時,如:code
# 表TB1001有主鍵索引PRIMARY KEY(ID) # 表TB1001有輔助索引IDX_C1(C1) 和輔助索引IDC_C2(C2) SELECT * FROM TB1001 WHERE C1='XXX' AND C2='XXX';
不使用Index Merge Intersect算法時執行計劃僞代碼爲:blog
SELECT * FROM TB1001 WHERE ID IN ( SELECT ID FROM TB1001 WHERE C1='XXX') AND C2='XXX';
使用Index Merge Intersect算法時執行計劃僞代碼爲:排序
SELECT T2.* FROM ( SELECT ID FROM TB1001 WHERE C1='XXX' INTERSECT SELECT ID FROM TB1001 WHERE C2='XXX' ) AS T1 INNER JOIN TB1001 AS T2 ON T1.ID=T2.ID;
操做成本假設1:索引
假設: 知足C1='XXX'的記錄有10000行:索引IDX_C1上每一個數據頁存放500行索引記錄,知足條件數據: A、"順序存放"在索引IDX_C1上"連續"的20個索引頁中。 B、"分散存放"在主鍵上"隨機"的2000個數據頁中。 知足C2='XXX'的記錄有20000行,索引IDX_C2上每一個數據頁存放500行索引記錄,知足條件數據: A、"順序存放"在索引IDX_C2上"連續"的40個索引頁中。 B、"分散存放"在主鍵上"隨機"的4000個數據頁中。 同時知足C1='XXX' AND C2='XXX'的記錄有200行,知足條件數據: A、"分散存放"在主鍵上"隨機"的40個數據頁中 那麼: 1、不使用Index Merge Intersect算法須要"順序讀取"20個IDX_C1索引頁+"隨機讀取"2000個主鍵索引數據頁 2、使用Index Merge Intersect算法須要"順序讀取"20個IDX_C1索引頁+"順序讀取"40個IDX_C2索引頁+"隨機讀取"40個主鍵索引數據頁 針對上面狀況,使用Index Merge Intersect算法能有效下降對主鍵的回表查找次數和隨機讀取次數(從2000次降低至40次)。
操做成本假設2:文檔
假設: 知足C1='XXX'的記錄有20行:索引IDX_C1上每一個數據頁存放500行索引記錄,知足條件數據: A、"順序存放"在索引IDX_C1上"連續"的1個索引頁中。 B、"分散存放"在主鍵上"隨機"的20個數據頁中。 知足C2='XXX'的記錄有200000行,索引IDX_C2上每一個數據頁存放500行索引記錄,知足條件數據: A、"順序存放"在索引IDX_C2上"連續"的400個索引頁中。 B、"分散存放"在主鍵上"隨機"的40000個數據頁中。 同時知足C1='XXX' AND C2='XXX'的記錄有19行,知足條件數據: A、"分散存放"在主鍵上"隨機"的19個數據頁中 那麼: 1、不使用Index Merge Intersect算法須要"順序讀取"1個IDX_C1索引頁+"隨機讀取"20個主鍵索引數據頁 2、使用Index Merge Intersect算法須要"順序讀取"1個IDX_C1索引頁+"順序讀取"400個IDX_C2索引頁+"隨機讀取"19個主鍵索引數據頁 針對上面狀況,使用Index Merge Intersect算法須要額外讀取400個IDX_C2索引頁才能下降1次主鍵的回表查詢和隨機讀取,顯然性能更差。
Index Merge Intersect算法和Index condition Pushdown特性
在MySQL官方文檔中,Index Merge Intersect算法能夠應用在分別使用主鍵和二級索引的查詢中,如:
SELECT * FROM innodb_table WHERE primary_key < 10 AND key_col1 = 20;
在未引入ICP特性的早期MySQL版本中,主鍵上過濾條件(primary_key < 10)不會"下推"到查詢知足key_col1 = 20條件的過程當中,所以可使用Index Merge Intersect算法來減小回表查找次數。
在引入ICP特性的MySQL版本中,因爲輔助索引的索引記錄中都包含主鍵列數據,所以主鍵上過濾條件(primary_key < 10)能夠"下推"到查詢知足key_col1 = 20條件的過程當中,無需再使用Index Merge Intersect算法。
## 在MySQL 5.7版本中測試 SELECT * FROM TB001 WHERE C1=10 AND ID<100; ## 執行計劃爲: *************************** 1. row *************************** id: 1 select_type: SIMPLE table: TB001 partitions: NULL type: ref possible_keys: PRIMARY,IDX_C1 key: IDX_C1 key_len: 5 ref: const rows: 1 filtered: 33.33 Extra: Using where; Using index ## 執行計劃Extra部分沒有INDEX MERGE相關信息
Index Merge Intersect性能問題優化
在部分場景中,使用Index Merge Intersec算法會帶來嚴重的性能問題,DBA能夠經過MySQL參數optimizer_switch來關閉該特性。
對於經過Index Merge Intersec算法受益的查詢,能夠考慮使用組合索引或覆蓋索引來替換單列索引。
如對上面查詢,能夠將索引IDX_C1(C1)調整爲IDX_C1_C2(C1,C2),其查詢性能更佳。
Index Merge Union算法
當查詢過濾條件(WHERE部分)上使用OR關聯多個不一樣KEY的過濾條件時,如:
# 表TB1001有主鍵索引PRIMARY KEY(ID) # 表TB1001有輔助索引IDX_C1(C1) 和輔助索引IDC_C2(C2) SELECT * FROM TB1001 WHERE C1='XXX' OR C2='XXX';
其操做步驟爲:
1、使用IDX_C1索引獲取到知足條件的[C1,ID]記錄,記錄默認按照ID排序 2、使用IDX_C1索引獲取到知足條件的[C1,ID]記錄,記錄默認按照ID排序 3、將已經按照ID排序的步驟1和步驟2的數據進行合併去重ID。 4、按照ID回表查找並返回
僞代碼爲:
SELECT T2.* FROM ( SELECT ID FROM TB1001 WHERE C1='XXX' UNION SELECT ID FROM TB1001 WHERE C2='XXX' ) AS T1 INNER JOIN TB1001 AS T2 ON T1.ID=T2.ID 在建立索引IDX_C1(ID)時,其等價爲IDX_C1(C1,ID),相同C1值的記錄按ID值排序,所以UNION操做的兩個中見結果集在ID上時有序的。
Index Merge Sort-Union算法
當查詢過濾條件(WHERE部分)上使用OR關聯多個不一樣KEY的過濾條件時,如:
# 表TB1001有主鍵索引PRIMARY KEY(ID) # 表TB1001有輔助索引IDX_C1(C1) 和輔助索引IDC_C2(C2) SELECT * FROM TB1001 WHERE C1>'XXX' OR C2<'XXX';
其操做步驟爲:
1、使用IDX_C1索引獲取到知足條件的[C1,ID]記錄,再按照ID進行排序 2、使用IDX_C1索引獲取到知足條件的[C1,ID]記錄,再按照ID進行排序 3、將步驟1和步驟2的已按ID排序後數據進行合併去重ID。 4、按照ID回表查找並返回
僞代碼爲:
SELECT T2.* FROM ( SELECT ID FROM TB1001 WHERE C1>'XXX' ORDER BY ID UNION SELECT ID FROM TB1001 WHERE C2>'XXX' ORDER BY ID ) AS T1 INNER JOIN TB1001 AS T2 ON T1.ID=T2.ID 在建立索引IDX_C1(ID)時,其等價爲IDX_C1(C1,ID),對C1列進行範圍查詢返回數據的數據按照C1+ID排序,在ID列上是無序的,所以UNION操做前需先對兩個中間結果集排序。
Index Merge Union相關優化
在禁用Index Merge特性時,能夠經過SQL將OR操做改寫爲UNION ALL操做,使查詢同時使用多個索引。
如上面使用Index Merge Union算法的查詢,能夠改寫爲:
#改寫前: SELECT * FROM TB1001 WHERE C1='XXX' OR C2='XXX'; # 改寫後 SELECT T2.* FROM ( SELECT ID FROM TB1001 WHERE C1='XXX' UNION ALL SELECT ID FROM TB1001 WHERE C2='XXX' AND (C1<>'XXX' OR C1 IS NULL) ) AS T1 INNER JOIN TB1001 AS T2 ON T1.ID=T2.ID
PS: 將IDX_C2(C2)改寫爲IDX_C2_C2(C1,C2)能在UNION操做前避免回表查詢。