1. 程式人生 > >8.2.1.3 Range 優化

8.2.1.3 Range 優化


range access method 方法使用單個索引去取回資料表的子集,它包含一個或多個索引值區間.Rang它能對單個部分索引或者多個部分索引使用.以下章節給出詳細的描述
去解釋怎麼從where語句中解析區間。


1.The Range Access Method for Single-Part Indexes


對於單個部分索引,索引的值區間能夠方便的從where 條件語句中解析出,表示為範圍條件而不是區間.

對於單個部分索引的範圍條件定義如下.
1.對於所有的BTREE和HASH索引,key部分與常量值比較就是範圍條件,當使用 =,<=>,IN(),IS NULL,IS NOT NULL操作.
2.另外,對於BTREE索引,key部分與常量值比較就是範圍查詢,當樹勇 >, <, >=, <=, BETWEEN, !=, <>,或者LIKE,當LIKE的引數是一個常量並且不是用寬範圍(wildcard character)字元開始
3.對於所有的索引,由OR 和 AND組合的多個範圍條件形成一個範圍條件
常量值定義如下
1.來自查詢字串的一個常量
2.來自 same join 常量表(const table)或者系統表(system table)的列
3.無關聯子查詢的結果集
4.完全由前面型別組成的任意子表示式

以下是一些在where語句中使用範圍條件的樣例查詢
SELECT * FROM t1 WHERE key_col > 1 AND key_col < 10;

SELECT * FROM t1 WHERE key_col =1 OR key_col IN (15,18,20);

SELECT * FROM t1 WHERE key_col LIKE 'ab%' OR key_col BETWEEN 'bar' AND 'foo';

一些非常量值或者會在優化器進行常量傳播階段被轉換成常量

MySQL 對每個可能的索引都會盡力從WHERE語句中提取範圍條件.在提取階段,不能用於構造範圍條件的條件將會被丟棄,
生成重疊範圍的條件將會被組合,生成空範圍的條件將會被移除.

考慮下面這個語句,key1是一個被索引的列,nonkey不是一個被索引的列
SELECT * FROM t1 WHERE
(key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
(key1 < 'bar' AND nonkey =4) OR
(key1 < 'uux' AND key1 > 'z');

對key1的提取過程如下

1.從原始的WHERE語句開始
(key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
(key1 < 'bar' AND nonkey =4) OR
(key1 < 'uux' AND key1 > 'z');

2.移除nonkey =4 和 key1 LIKE '%b',因為他們不能用於範圍掃描.移除他們的正確姿勢是把他們替換成TRUE,這樣我們在進行範圍掃描的時候就不會漏資料.
把他們替換成TRUE,我們的語句將會變成如下這樣.
(key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR
(key1 < 'bar' AND TRUE) OR
(key1 < 'uux' AND key1 > 'z');

3.找出總是true或者false 的條件
(key1 LIKE 'abcde%' OR TRUE) 總是 TRUE
(key1 < 'uux' AND key1 > 'z') 總是false
使用true或false替換他們,我們得到如下語句
(key1 < 'abc' AND TRUE) OR (key1 < 'bar' AND TRUE) OR (FALSE)
移除不必要的TRUE和FALSE常量.
(key1 < 'abc') OR (key1 < 'bar')
4.組合重複覆蓋的區間得到一個最終的條件用於範圍掃描
(key1 < 'bar')

通常(比如前面展示的列子),用於範圍掃描的條件比WHERE 語句更加寬鬆.MySQL 執行一個附加的檢查來過濾行,過濾掉的行滿足RANG條件但是不滿足滿足WHERE 語句.

range條件提取演算法能夠提取巢狀任意深度的AND/OR組合,並且輸出不依賴AND/OR出現在where語句中的順序.

MySQL 不支援合併多個範圍對於空間索引.為了繞過這個限制, 你可以對相同的SELECT使用UNION,除非你每個空間SELECT語句不同.

 

2.The Range Access Method for Multiple-Part Indexes


對多部分索引使用Range conditions是對單部分索引的擴充套件.多個部分索引上的範圍條件嚴格索引落在一個或多個關鍵字元組行的內部.
關鍵字元組範圍定義在關鍵字集合上,根據索引順序.

比如,考慮一個多部分索引 key1(key_part1,key_part2,key_part3), 以下的key元組集合以關鍵字順序列出
key_part1 key_part2 key_part3
NULL  1   'abc'
NULL  1   'xyz'
NULL  2   'foo'
1         1   'abc'
1         1   'xyz'
1         2   'abc'
2         1  'aaa'

key_part1 = 1 條件定義以下的範圍

(1,-inf,-inf) <= (key_part1,key_part2,key_part3) < (1,+inf,+inf)

這個範圍覆蓋先前第4,第5,第6元組,並且能被range access method 使用.

通過對比,條件 key_part3 = ‘abc’ 沒有定義一個單區間,也不能在range access method中使用.


以下的描述更加詳細說明多部分索引範圍條件工作原理.

對於Hash索引,包含同樣值的每個區間都能夠被使用.這個就意味著區間只能夠被用於以下條件.
key_part1 cmp const1
AND key_part2 cmp const2
AND ...
AND key_partN cmp constN;

在這裡,const1,const2,...都是常量,cmp 是=,<=>,or IS NULL操作符中的一個,條件覆蓋索引的所有部分.
比如,以下範圍條件是三個部分的hash索引
key_part1 =1 AND key_part2 IS NULL AND key_part3 = 'foo'

哪些定義會被視為常量,檢視 The Range Access Method for Single-Part Indexes.


對於BTREE 索引,條件能使用的區間由一個或多個AND組成,每個條件使用常量值比較key的一部分,使用 =,<=>,IS NULL,>,<,>=,<=,!=,<>,
BETWEEN,或者 LIKE 'pattern'(pattern不能是萬用字元開頭).每個區間同樣可能用於決定單個包含所有行的key元組,key元組匹配條件
(或者倆個區間,如果使用if <> 或 != )

當比較操作符是=,<=>,或者 is NULL,優化器儘可能使用key parts的其他部分去決定區間.如果操作符是>,<,>=,<=,!=,<>,BETWEEN,或者LIKE,優化器不再考慮更多的
key parts。對於下面的表示式,優化器使用來自第一個比較操作的 =.也同樣使用來自第二個比較的>=,但是不再考慮更多的key parts,也不會使用第三個比較符來構造
區間.


key_part1 ='foo' AND key_part2 >= 10 AND key_part3 > 10
單一的區間是
('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)

建立的區間包含的行可能比初始條件包含的行多很多.比如,先前的列子包含('foo',11,0),但是這個不包含在原始條件內.

 

如果覆蓋行集合的條件包含或的區間,它們形成一個UNION所有區間的條件.如果是由AND組成,形成的條件就是所有區間的交集.比如.對於下面在倆部分索引上的條件.

(key_part1 =1 AND key_part < 2) OR (key_part1 > 5)

形成的區間是
(1,-inf) < (key_part1,key_part2) < (1,2)
(5,-inf) < (key_part1,key_part2)

在這個例子裡,第一行的區間左邊界使用key的一部分,右邊界使用key的倆個部分.
第二行的區間僅使用key的一部分.EXPLAN語句輸出的key_len表示使用的最大的key字首.

在一些例子中,key_len可能表示使用key的部分,但是可能不是你所期望的那樣.考慮如下列子,key_part1和key_part2可能是NULL.然後key_len列
展示倆個key部分
key_part1 >= 1 and key_part2 < 2

但是,實際上,條件被轉成如下條件

key_part1 >= 1 AND key_part2 IS NOT NULL
The Range Access Method for Single-Part Indexes章節描述了優化器對範圍條件單部分索引怎麼組合和排除區間.對於多部分條件索引與其相似.

 

多值比較的等值範圍優化
考慮如下表達式,col_name 是一個被索引的列
col_name IN (val1,...,vall1N)
col_name = val1 OR ... OR col_name =valN

如果col_name等於其中的一個或多個,那麼這倆個表示式都是true.這種比較等價於範圍比較(這裡的範圍表示單一值).優化器根據一下規則估計等價範圍比較讀取行的花費.

1.如果col_name 是unique 索引,對每個範圍行估價為 1 ,因為最多隻有一行包含給定的值.

2.要不然,col_name是非unique,優化器對每個範圍估計行總數,使用索引統計資訊或者(dives into the index) dives into the index(這個是什麼???)

對於index dives,對每個結束的範圍優化器都做一次dive,使用範圍內行的數目做預估.比如,col_name IN (10,20,30)這個查詢三個等價範圍,優化器對每個範圍做倆次
dive來生成行估價值.每對dive產出一個行數目來做為估價值.

索引Index dives 加速行估價,但是隨著比較值的數目增加,優化器使用生成行估價花費長時間.使用索引統計資訊雖然加速比index dives慢,但是可以更快生成
行估價對於大量的值.

你可以配置 eq_range_index_dive_limit 系統變數的值,以此控制優化器行估價策略。對於 N 個等價範圍 為了允許使用 index dive,設定 eq_range_index_dive_limit
為 N +1. 為了關閉使用索引統計資訊,總是使用index dives ,設定 eq_range_index_dive_limit 為 0.

eq_range_index_dive_limit 在5.6.5中可用,在5.6.5之前,優化器使用index dive,這就是等價於 eq_range_index_dive_limit =0.

為了更好的計算代價,使用 ANALYZE TABLE更新表索引統計資訊,