MySQL Execute Plan--Index Merge特性

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三種合併算法:優化

1Index Merge Intersect:對多個結果集求交集
2Index Merge Union:對多個結果集求UNION集合(無需對結果集排序)
3Index 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操做前避免回表查詢。

相關文章
相關標籤/搜索