Index Condition Pushdown(ICP)是針對mysql使用索引從表中檢索行數據時的一種優化方法。
在沒有ICP特性之前,存儲引擎根據索引去基表查找並將數據返回給mysql server,mysql server再根據where條件進行數據過濾。
有了ICP之后,在取出索引的同時,判斷是否可以根據索引中的列進行where條件過濾,也就是將where的部分過濾操作放在了存儲引擎層。這樣就會減少上層sql層對記錄的獲取。
ICP優化支持range、ref、eq_ref、ref_or_null類型的查詢。查詢優化器會給出相應的提示:Using index condition。當開啟ICP后,在執行計划的extra列會顯示:Using index condition。
ICP支持innodb、myisam表。對於innodb表,ICP只是用於輔助索引。
在5.6中,ICP不支持分區表。這個問題在mysql 5.7中得到解決。
優化器使用ICP時,server層將會把能夠通過使用索引進行評估的where條件下推到storage engine層。數據訪問和提取過程如下:
1) storage engine從索引中讀取下一條索引元組。
2) storage engine使用索引元組評估下推的索引條件。如果沒有滿足where條件,storage engine將會處理下一條索引元組(回到上一步)。只有當索引元組滿足下推的索引條件的時候,才會繼續去基表中讀取數據。
3) 如果滿足下推的索引條件,storage engine通過索引元組定位基表的行和讀取整行數據並返回給server層。
4) server層評估沒有被下推到storage engine層的where條件,如果該行數據滿足where條件則使用,否則丟棄。
沒有ICP之前:

開啟ICP之后,就變成:

默認是開啟ICP的,手動開啟/關閉ICP:
set optimizer_switch = 'index_condition_pushdown=off'; set optimizer_switch = 'index_condition_pushdown=on';
測試過程
1.環境准備#mysql 5.6.25 #關閉結果緩存 mysql> set global query_cache_size=0; mysql> set query_cache_type=off; #查看表結構 mysql> show create table employees\G *************************** 1. row *************************** Table: employees Create Table: CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`), KEY `idx_first_last_name` (`first_name`,`last_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) mysql>
2.開啟ICP后進行測試
mysql> set profiling = 1; mysql> select * from employees where first_name='Anneke' and last_name like '%sig' ; mysql> explain select * from employees where first_name='Anneke' and last_name like '%sig' ; mysql> show profiles; mysql> show profile cpu,block io for query 1;
3.關閉ICP后進行測試
mysql> set optimizer_switch='index_condition_pushdown=off'; mysql> set profiling = 1; mysql> select * from employees where first_name='Anneke' and last_name like '%sig' ; mysql> explain select * from employees where first_name='Anneke' and last_name like '%sig' ; mysql> show profiles; mysql> show profile cpu,block io for query 1;
4.結果比較
開啟ICP后的執行計划:執行計划中extra部分的內容是"using index condition"
mysql> explain select * from employees where first_name='Anneke' and last_name like '%sig' ; +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-----------------------+ | 1 | SIMPLE | employees | ref | idx_first_last_name | idx_first_last_name | 44 | const | 224 | Using index condition | +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-----------------------+
關閉ICP后的執行計划:執行計划中extra部分的內容是"using where"
mysql> explain select * from employees where first_name='Anneke' and last_name like '%sig' ; +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-------------+ | 1 | SIMPLE | employees | ref | idx_first_last_name | idx_first_last_name | 44 | const | 224 | Using where | +----+-------------+-----------+------+---------------------+---------------------+---------+-------+------+-------------+
開啟ICP后的profile內容:Sending data部分的值是0.000212s
mysql> show profile cpu,block io for query 1; +----------------------+----------+----------+------------+--------------+---------------+ | Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | +----------------------+----------+----------+------------+--------------+---------------+ | starting | 0.000114 | 0.000000 | 0.000000 | 0 | 0 | | checking permissions | 0.000007 | 0.000000 | 0.000000 | 0 | 0 | | Opening tables | 0.000018 | 0.000000 | 0.000000 | 0 | 0 | | init | 0.000034 | 0.000000 | 0.000000 | 0 | 0 | | System lock | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | | optimizing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | | statistics | 0.000383 | 0.000000 | 0.000000 | 0 | 0 | | preparing | 0.000019 | 0.000000 | 0.000000 | 0 | 0 | | executing | 0.000002 | 0.000000 | 0.000000 | 0 | 0 | | Sending data | 0.000212 | 0.000000 | 0.000000 | 0 | 0 | | end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | | query end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | | closing tables | 0.000006 | 0.000000 | 0.000000 | 0 | 0 | | freeing items | 0.000020 | 0.000000 | 0.000000 | 0 | 0 | | cleaning up | 0.000011 | 0.000000 | 0.000000 | 0 | 0 | +----------------------+----------+----------+------------+--------------+---------------+
關閉ICP后的profile內容:Sending data部分的值是0.010990s
mysql> show profile cpu,block io for query 1; +----------------------+----------+----------+------------+--------------+---------------+ | Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | +----------------------+----------+----------+------------+--------------+---------------+ | starting | 0.000165 | 0.000000 | 0.000000 | 0 | 0 | | checking permissions | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | | Opening tables | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | | init | 0.000039 | 0.000000 | 0.000000 | 0 | 0 | | System lock | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | | optimizing | 0.000037 | 0.001000 | 0.000000 | 0 | 0 | | statistics | 0.000483 | 0.001000 | 0.000000 | 0 | 0 | | preparing | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | | executing | 0.000002 | 0.000000 | 0.000000 | 0 | 0 | | Sending data | 0.010990 | 0.007999 | 0.002000 | 0 | 0 | | end | 0.000009 | 0.000000 | 0.000000 | 0 | 0 | | query end | 0.000005 | 0.000000 | 0.000000 | 0 | 0 | | closing tables | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | | freeing items | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | | cleaning up | 0.000014 | 0.000000 | 0.000000 | 0 | 0 | +----------------------+----------+----------+------------+--------------+---------------+
其它:
當sql使用覆蓋索引時,不支持ICP優化方法
mysql> explain select first_name,last_name from employees where first_name='Anneke' and last_name='Porenta' ; +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+ | 1 | SIMPLE | employees | ref | idx_first_last_name | idx_first_last_name | 94 | const,const | 1 | Using where; Using index | +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+ mysql> explain select * from employees where first_name='Anneke' and last_name='Porenta' ; +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+-----------------------+ | 1 | SIMPLE | employees | ref | idx_first_last_name | idx_first_last_name | 94 | const,const | 1 | Using index condition | +----+-------------+-----------+------+---------------------+---------------------+---------+-------------+------+-----------------------+