http://blog.itpub.net/22664653/viewspace-1210844/ -- 這篇博客寫的更細,以后看
ICP(index condition pushdown)是mysql利用索引(二級索引)元組和篩字段在索引中的where條件從表中提取數據記錄的一種優化操作。ICP的思想是:存儲引擎在訪問索引的時候檢查篩選字段在索引中的where條件(pushed index condition,推送的索引條件),如果索引元組中的數據不滿足推送的索引條件,那么就過濾掉該條數據記錄。ICP(優化器)盡可能的把index condition的處理從server層下推到storage engine層。storage engine使用索引過過濾不相關的數據,僅返回符合index condition條件的數據給server層。也是說數據過濾盡可能在storage engine層進行,而不是返回所有數據給server層,然后后再根據where條件進行過濾。使用ICP(mysql 5.6版本以前)和沒有使用ICP的數據訪問和提取過程如下(插圖來在MariaDB Blog):
優化器沒有使用ICP時,數據訪問和提取的過程如下:
1) 當storage engine讀取下一行時,首先讀取索引元組(index tuple),然后使用索引元組在基表中(base table)定位和讀取整行數據。
2) sever層評估where條件,如果該行數據滿足where條件則使用,否則丟棄。
3) 執行1),直到最后一行數據。
優化器使用ICP時,server層將會把能夠通過使用索引進行評估的where條件下推到storage engine層。數據訪問和提取過程如下:
1) storage engine從索引中讀取下一條索引元組。
2) storage engine使用索引元組評估下推的索引條件。如果沒有滿足wehere條件,storage engine將會處理下一條索引元組(回到上一步)。只有當索引元組滿足下推的索引條件的時候,才會繼續去基表中讀取數據。
3) 如果滿足下推的索引條件,storage engine通過索引元組定位基表的行和讀取整行數據並返回給server層。
4) server層評估沒有被下推到storage engine層的where條件,如果該行數據滿足where條件則使用,否則丟棄。
而使用ICP時,如果where條件的一部分能夠通過使用索引中的字段進行評估,那么mysql server把這部分where條件下推到storage engine(存儲引擎層)。存儲引擎通過索引元組的索引列數據過濾不滿足下推索引條件的數據行。
索引條件下推的意思就是篩選字段在索引中的where條件從server層下推到storage engine層,這樣可以在存儲引擎層過濾數據。由此可見,ICP可以減少存儲引擎訪問基表的次數和mysql server訪問存儲引擎的次數。
注意一下ICP的使用條件:
- 只能用於二級索引(secondary index)。
- explain顯示的執行計划中type值(join 類型)為range、 ref、 eq_ref或者ref_or_null。且查詢需要訪問表的整行數據,即不能直接通過二級索引的元組數據獲得查詢結果(索引覆蓋)。
- ICP可以用於MyISAM和InnnoDB存儲引擎,不支持分區表(5.7將會解決這個問題)。
ICP的開啟優化功能與關閉
MySQL5.6可以通過設置optimizer_switch([global|session],dynamic)變量開啟或者關閉index_condition_push優化功能,默認開啟。
mysql > set optimizer_switch=’index_condition_pushdown=on|off’
用explain查看執行計划時,如果執行計划中的Extra信息為“using index condition”,表示優化器使用的index condition pushdown。
在mysql5.6以前,還沒有采用ICP這種查詢優化,where查詢條件中的索引條件在某些情況下沒有充分利用索引過濾數據。假設一個組合索引(多列索引)K包含(c1,c2,…,cn)n個列,如果在c1上存在范圍掃描的where條件,那么剩余的c2,…,cn這n-1個上索引都無法用來提取和過濾數據(不管不管是唯一查找還是范圍查找),索引記錄沒有被充分利用。即組合索引前面字段上存在范圍查詢,那么后面的部分的索引將不能被使用,因為后面部分的索引數據是無序。比如,索引key(a,b)中的元組數據為(0,100)、(1,50)、(1,100) ,where查詢條件為 a < 2 and b = 100。由於b上得索引數據並不是連續區間,因為在讀取(1,50)之后不再會讀取(1,100),mysql優化器在執行索引區間掃描之后也不再掃描組合索引其后面的部分。
表結構定義如下:
|
1
2
3
4
5
6
7
8
9
10
11
|
CREATE
TABLE
`person` (
`person_id`
smallint
(5) unsigned
NOT
NULL
AUTO_INCREMENT,
`postadlcode`
int
(11)
DEFAULT
NULL
,
`age` tinyint(4)
DEFAULT
NULL
,
`first_name`
varchar
(45)
NOT
NULL
,
`last_name`
varchar
(45)
NOT
NULL
,
`last_update`
timestamp
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
PRIMARY
KEY
(`person_id`),
KEY
`idx_p_a` (`postadlcode`,`age`),
KEY
`idx_f_l` (`first_name`,`last_name`)
) ENGINE=InnoDB
DEFAULT
CHARSET=utf8
|
關閉ICP優化,Extra信息為“Using Where”
|
1
2
3
4
5
6
7
|
mysql>
set
optimizer_switch =
"index_condition_pushdown=off"
;
mysql> explain
select
*
from
person
where
postadlcode
between
300000
and
400000
and
age > 40;
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| Extra |
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | person | range | idx_p_a | idx_p_a | 7 |
NULL
| 21 | Using
where
|
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|
開啟ICP之后,Extra信息為“Using Index Condition”
|
1
2
3
4
5
6
7
|
mysql>
set
optimizer_switch =
"index_condition_pushdown=on"
;
mysql> explain
select
*
from
person
where
postadlcode
between
300000
and
400000
and
age > 40;
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-----------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| Extra |
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-----------------------+
| 1 | SIMPLE | person | range | idx_p_a | idx_p_a | 7 |
NULL
| 21 | Using
index
condition |
+
----+-------------+--------+-------+---------------+---------+---------+------+------+-----------------------+
|


