1.system
2.const
·
·
3.eq_ref
4.ref
5.ref_or_null
6.index_merge
where条件中的列都有索引
·
·
·
7.range
8.index
9.all
测试表: mysql> desc sbtest1; +-------+-----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | k | int(11) | NO | MUL | 0 | | | c | char(120) | NO | | | | | pad | char(60) | NO | | | | +-------+-----------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
测试表索引: mysql> show index from sbtest1; +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | sbtest1 | 0 | PRIMARY | 1 | id | A | 4377009 | NULL | NULL | | BTREE | | | | sbtest1 | 1 | k_1 | 1 | k | A | 1046316 | NULL | NULL | | BTREE | | | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
执行计划: mysql> explain select * from sbtest1 where k=2506783; +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | SIMPLE | sbtest1 | NULL | ref | k_1 | k_1 | 4 | const | 91 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
测试准备:
CREATE TABLE hero ( id INT, name VARCHAR(100), country varchar(100), PRIMARY KEY (id), KEY idx_name (name) ) Engine=InnoDB CHARSET=utf8; 我们为hero表的id列创建了聚簇索引,为name列创建了一个二级索引。
INSERT INTO hero VALUES (1, 'l刘备', '蜀'), (3, 'z诸葛亮', '蜀'), (8, 'c曹操', '魏'), (15, 'x荀彧', '魏'), (20, 's孙权', '吴');
mysql> EXPLAIN SELECT * FROM hero WHERE name < 's孙权' AND country = '蜀'; +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ | 1 | SIMPLE | hero | NULL | range | idx_name | idx_name | 303 | NULL | 2 | 20.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ 1 row in set, 1 warning (0.03 sec)
1.server层第一次开始执行查询,把条件name < 's孙权'交给存储引擎,让存储引擎定位符合条件的第一条记录。 2.存储引擎在二级索引idx_name中定位name < 's孙权'的第一条记录,很显然符合该条件的二级索引记录的name列的值为'c曹操'。然后需要注意,我们看到EXPLAIN语句的输出结果的Extra列有一个Using index condition的提示,这表明会将有关idx_name二级索引的查询条件放在存储引擎层判断一下,这个特性就是所谓的索引条件下推(Index Condition Pushdown,简称ICP)。很显然这里的ICP条件就是name < 's孙权'。有的同学可能会问这不就是脱了裤子放屁么,name值为'c曹操'的这条记录就是通过name < 's孙权'这个条件定位的,为啥还要再判断一次?这就是设计MySQL 的大叔的粗暴设计,十分简单,没有为啥。然后拿着该二级索引记录中的主键值去回表,把完整的用户记录都取到之后返回给server层(也就是说得到一条二级索引记录后立即去回表,而不是把所有的二级索引记录都拿到后统一去回表)。 小贴士: 对于使用二级索引进行等值查询的情况有些许不同,比方说上边的条件换成`name = 's孙权'`,对于等值查询的这种情况,设计MySQL的大叔在InnoDB存储引擎层有特殊的处理方案,是不作为ICP条件进行处理的。 3.我们的执行计划输出的Extra列有一个Using Where的提示,意味着server层在接收到存储引擎层返回的记录之后,接着就要判断其余的WHERE条件是否成立(就是再判断一下country = '蜀'是否成立)。如果成立的话,就直接发送给客户端。如果不成立的话,就跳过该条记录。 小贴士: 什么?发现一条记录符合条件就发送给了客户端?那为什么我的客户端不是一条一条的显示查询结果,而是一下子全部展示呢?这是客户端软件的鬼,人家规定在接收完全部的记录之后再展示而已。 4.接着server层向存储引擎层要求继续读刚才那条记录的下一条记录。 5.因为每条记录的头信息中都有next_record的这个属性,所以可以快速定位到下一条记录的位置,然后继续判断ICP条件,存储引擎把下一条记录取出后就将其返回给server层。 6.然后重复第3步的过程,直到存储引擎层遇到了不符合name < 's孙权'的记录,然后向server层返回了读取完毕的信息,这是server层将结束查询。
first_read = true; //是否是第一次读取 while (true) { if (first_read) { first_read = false; err = index_read(...); //调用存储引擎接口,定位到第一条符合条件的记录; } else { err = index_next(...); //调用存储引擎接口,读取下一条记录 } if (err = 存储引擎的查询完毕信息) { break; //结束查询 } if (是否符合WHERE条件) { send_data(); //将该记录发送给客户端; } else { //跳过本记录 } }