首先回顾一下这三个的定义
ICP有以下条件限制:
是否启用ICP
SET optimizer_switch = ‘index_condition_pushdown=off’;
SET optimizer_switch = ‘index_condition_pushdown=on’;
数据库来自MySQL官方employees库
表结构如下:
MySQL8.0.19
CREATE TABLE `employees` ( `emp_no` int 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_all` (`birth_date`,`last_name`,`first_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
birth_date, last_name, first_name三列联合索引,编码utf8
explain select emp_no, first_name, last_name from employees; +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | employees | NULL | index | NULL | index_all | 97 | NULL | 299423 | 100.00 | Using index | +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+-------------+
type为index,扫描整个索引树,服务器层不需要再判断条件,所以Extra只有Using index
使用三列,key_len = 3 + (14 * 3 + 2) + (16 * 3 + 2) = 97
explain select emp_no, last_name from employees where birth_date = '1999-09-09'; +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | employees | NULL | ref | index_all | index_all | 3 | const | 1 | 100.00 | Using index | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+
满足最左匹配原则,且非唯一索引,故type为ref,走覆盖索引,Using index
key_len = 3
explain select emp_no, first_name from employees where last_name like '%达到%'; +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+--------------------------+ | 1 | SIMPLE | employees | NULL | index | NULL | index_all | 97 | NULL | 299423 | 11.11 | Using where; Using index | +----+-------------+-----------+------------+-------+---------------+-----------+---------+------+--------+----------+--------------------------+
where条件不满足最左匹配,但是select的列在索引树中可以全部找到,故Using index,并且type为index,存储引擎扫描整个索引树,找到满足last_name like '%达到%'的数据(where条件有效),返回给服务器,服务器再过滤不需要的字段。
因为使用了整个联合索引,key_len = 97
如果select了不在联合索引的字段,则不能使用覆盖索引,只有Using where了。
explain select emp_no, first_name, hire_date from employees where birth_date = '199-09-09' and last_name like '%达到%'; +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | index_all | index_all | 3 | const | 1 | 11.11 | Using index condition | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+
首先明确一点,这条SQL必须访问基本表,因为有hire_date字段,所以一定没有Using index。
满足最左匹配,birth_date字段走索引,但是last_name不能走索引。
在有ICP的情况下,last_name的判断由存储引擎在索引树上完成,返回给服务器后,因为有hire_date字段,服务器访问基本表获取所有数据。
当没有ICP情况下,last_name由服务器完成判断,只有Using where,服务器读取基本表的部分行可能是不满足条件的。
注意:Using index condition下推的条件并没有用到索引,而是全部扫描,所以key_len不计算在内,所谓用到索引,就是利用了B+树查找的特性
+----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | employees | NULL | ref | index_all | index_all | 3 | const | 1 | 11.11 | Using where | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+
explain select emp_no, first_name from employees where birth_date = '199-09-09' and last_name like '%达到%'; +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+ | 1 | SIMPLE | employees | NULL | ref | index_all | index_all | 3 | const | 1 | 11.11 | Using where; Using index | +----+-------------+-----------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+
没有hire_date,服务器不需要访问基本表,ICP优化也就没必要了。