你的位置:首页 > 数据库

[数据库]【mysql】关于Index Condition Pushdown特性


关于ICP

Index Condition Pushdown (ICP) is an optimization for the case where MySQL retrieves rows from a table using an index. Without ICP, the storage engine traverses the index to locate rows in the base table and returns them to the MySQL server which evaluates the WHEREcondition for the rows. With ICP enabled, and if parts of the WHERE condition can be evaluated by using only fields from the index, the MySQL server pushes this part of the WHERE condition down to the storage engine. The storage engine then evaluates the pushed index condition by using the index entry and only if this is satisfied is the row read from the table. ICP can reduce the number of times the storage engine must access the base table and the number of times the MySQL server must access the storage engine.

也就说:利用索引来过滤一部分where条件

测试

导入数据库

wget https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2tar jxf employees_db-full-1.0.6.tar.bz2cd employees_dbmysql -uroot -p < employees.sql

表结构

mysql> show create table employees \G*************************** 1. row ***************************    Table: employeesCreate 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 `index_bh` (`birth_date`,`hire_date`)) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)

一些表数据

mysql> select @@optimizer_switch like '%index_condition_pushdown%' \G*************************** 1. row ***************************@@optimizer_switch like '%index_condition_pushdown%': 11 row in set (0.00 sec)mysql> select @@optimizer_switch like '%index_condition_pushdown%' \G*************************** 1. row ***************************@@optimizer_switch like '%index_condition_pushdown%': 11 row in set (0.00 sec)mysql> select @@query_cache_type;+--------------------+| @@query_cache_type |+--------------------+| OFF        |+--------------------+1 row in set (0.01 sec)mysql> select count(*) from employees;+----------+| count(*) |+----------+|  300024 |+----------+1 row in set (0.17 sec)mysql> set profiling=1;Query OK, 0 rows affected, 1 warning (0.00 sec)

建立索引

alter table employees add index index_bh (`birth_date`,`hire_date`);

 

查询分析

mysql> explain select *  from employees where birth_date between '1955-01-01' and '1955-12-31' and datediff(hire_date,birth_date)>12300 +----+-------------+-----------+-------+---------------+----------+---------+------+-------+-------------+| id | select_type | table   | type | possible_keys | key   | key_len | ref | rows | Extra    |+----+-------------+-----------+-------+---------------+----------+---------+------+-------+-------------+| 1 | SIMPLE   | employees | range | index_bh   | index_bh | 3    | NULL | 46318 | Using where |+----+-------------+-----------+-------+---------------+----------+---------+------+-------+-------------+1 row in set (0.00 sec)mysql> SET optimizer_switch='index_condition_pushdown=on';Query OK, 0 rows affected (0.00 sec)mysql> explain select *  from employees where birth_date between '1955-01-01' and '1955-12-31' and datediff(hire_date,birth_date)>12300 and first_name like 'S%b%';+----+-------------+-----------+-------+---------------+----------+---------+------+-------+------------------------------------+| id | select_type | table   | type | possible_keys | key   | key_len | ref | rows | Extra               |+----+-------------+-----------+-------+---------------+----------+---------+------+-------+------------------------------------+| 1 | SIMPLE   | employees | range | index_bh   | index_bh | 3    | NULL | 46318 | Using index condition; Using where |+----+-------------+-----------+-------+---------------+----------+---------+------+-------+------------------------------------+1 row in set (0.01 sec)

执行查询

mysql> show profiles;                                                         +----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------+| Query_ID | Duration  | Query                                                                        |+----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------+|    1 | 0.00278025 | desc employees                                                                    ||    2 | 0.00049775 | show create table employees                                                             ||    3 | 0.07444550 | select *  from employees where birth_date between '1955-01-01' and '1955-12-31' and datediff(hire_date,birth_date)>12300 and first_name like 'S%b%' ||    4 | 0.00027500 | SET optimizer_switch='index_condition_pushdown=off'                                                 ||    5 | 0.12347025 | select *  from employees where birth_date between '1955-01-01' and '1955-12-31' and datediff(hire_date,birth_date)>12300 and first_name like 'S%b%' |+----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------+

从结果可以看出来开启ICP之后确实快不少

启用ICP之后,可以用索引来筛选 datediff(hire_date,birth_date)>12300 记录,不需要读出整条记录

原理如下图所示(图来自MariaDB)