MySQL索引扩展(Index Extensions)学习总结

 

MySQL InnoDB的二级索引(Secondary Index)会自动补齐主键,将主键列追加到二级索引列后面。详细一点来讲,InnoDB的二级索引(Secondary Index)除了存储索引列key值,还存储着主键的值(而不是指向主键的指针)。为何这样作呢?由于InnoDB是以汇集索引方式组织数据的存储,即主键值相邻的数据行紧凑的存储在一块儿(索引组织表)。当数据行移动或者发生页分裂的时候,能够减小大量的二级索引维护工做。InnoDB移动行时,无需更新二级索引。咱们以官方文档的例子来测试:html

 

CREATE TABLE t1 (
  i1 INT NOT NULL DEFAULT 0,
  i2 INT NOT NULL DEFAULT 0,
  d DATE DEFAULT NULL,
  PRIMARY KEY (i1, i2),
  INDEX k_d (d)
) ENGINE = InnoDB;

 

 

如上所示,这个t1表包含主键和二级索引k_d,二级索引k_d(d)的元组在InnoDB内部实际被扩展成(d,i1,i2),即包含主键值。所以在设计主键的时候,常见的一条设计原则是要求主键字段尽可能简短,以免二级索引过大(由于二级索引会自动补齐主键字段)。mysql

 

 

优化器会考虑扩展二级索引的主键列,肯定何时使用以及如何使用该索引。 这样能够产生更高效的执行计划和达到更好的性能。有很多博客介绍索引扩展是从MySQL5.6.9开始引入的。不过我的尚未在官方文档看到相关资料。sql

 

优化器能够用扩展的二级索引来进行ref,range,index_merge等类型索引访问(index access),松散的索引扫描(index sacns),链接和排序优化,以及min()/max()优化。缓存

 

 

 

 

咱们先来插入测试数据(脚原本自官方文档):oracle

 

 

INSERT INTO t1 VALUES
(1, 1, '1998-01-01'), (1, 2, '1999-01-01'),
(1, 3, '2000-01-01'), (1, 4, '2001-01-01'),
(1, 5, '2002-01-01'), (2, 1, '1998-01-01'),
(2, 2, '1999-01-01'), (2, 3, '2000-01-01'),
(2, 4, '2001-01-01'), (2, 5, '2002-01-01'),
(3, 1, '1998-01-01'), (3, 2, '1999-01-01'),
(3, 3, '2000-01-01'), (3, 4, '2001-01-01'),
(3, 5, '2002-01-01'), (4, 1, '1998-01-01'),
(4, 2, '1999-01-01'), (4, 3, '2000-01-01'),
(4, 4, '2001-01-01'), (4, 5, '2002-01-01'),
(5, 1, '1998-01-01'), (5, 2, '1999-01-01'),
(5, 3, '2000-01-01'), (5, 4, '2001-01-01'),
(5, 5, '2002-01-01');

 

#默认状况下,索引扩展(use_index_extensions)选项是开启的。能够在当前会话经过修改优化器开关optimizer_switch开启、关闭此选项。app

 

mysql> show variables like '%optimizer_switch%';
 
mysql> SET optimizer_switch = 'use_index_extensions=off';
Query OK, 0 rows affected (0.00 sec)
 
mysql> EXPLAIN
    -> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                    |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | t1    | ref  | PRIMARY,k_d   | k_d  | 4       | const |    5 | Using where; Using index |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

 

这种状况下,优化器不会使用主键,由于主键由字段(i1,i2)组成,可是该查询中没有引用t2字段;优化器会选择二级索引 k_d(d) 。性能

 

 

咱们将use_index_extensions选项在当前会话开启,那么SQL语句的执行计划会怎样变化呢?测试

 

mysql> SET optimizer_switch = 'use_index_extensions=on';
Query OK, 0 rows affected (0.00 sec)
 
mysql> EXPLAIN
    -> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref         | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | PRIMARY,k_d   | k_d  | 8       | const,const |    1 | Using index |
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
1 row in set (0.00 sec)
 
mysql> 

 

clip_image001

 

当use_index_extensions=off的时候,仅使用索引k_d中d列的数据,忽略了扩展的主键列的数据。而use_index_extensions=on时,使用了k_d索引中(i1,i2,d)三列的数据。能够从上面两种状况下的explain输出结果中信息得以验证。优化

 

key_len:由4变到8,说明不单单使用了d列上的索引,并且使用了扩展的主键i1列的数据this

 

ref:由const变为const,const, 使用了索引的两部分。

 

rows:从5变为1,代表InnoDB只须要检查更少的数据行就能够产生结果集。

 

Extra:Using index,Using where 变为Using index。经过索引覆盖就完成数据查询,而不须要读取任何的数据行。官方文档的介绍以下:

 

The Extra value changes from Using where; Using index to Using index. This means that rows can be read using only the index, without consulting columns in the data row.

 

其实关于这二者的区别,查了不少资料都没有完全搞清楚Using index,Using whereUsing index的区别。此处不作展开。

 

 

另外,从status信息中Handler_read_%相关状态值能够观察实际执行过程当中索引和数据行的访问统计。

 

    flush table  关闭已打开的数据表,并清除缓存(表缓存和查询缓存)。

    flush status 把status计数器清零。

 

 

Handler_read_key:The number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.

 

Handler_read_next:The number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.(此选项代表在进行索引扫描时,按照索引从数据文件里取数据的次数。)

 

 

 

关闭use_index_extensions状况下,status的统计信息

 

mysql> SET optimizer_switch = 'use_index_extensions=off';
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH TABLE t1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)
 
mysql> SHOW STATUS LIKE 'handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 1     |
| Handler_read_last     | 0     |
| Handler_read_next     | 5     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.00 sec)

 

开启use_index_extensions状况下,status的统计信息

 

mysql> SET optimizer_switch = 'use_index_extensions=on';
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH TABLE t1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)
 
mysql> SHOW STATUS LIKE 'handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 1     |
| Handler_read_last     | 0     |
| Handler_read_next     | 1     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.00 sec)
 
mysql> 

   

对比两个执行计划,发现Handler_read_next的值从5变为1,代表索引的访问效率更高了,减小了数据行的读取次数。

 

 

本文结合官方文档Use of Index Extensions和本身理解整理。

 

 

参考资料

 

https://docs.oracle.com/cd/E17952_01/mysql-5.6-en/index-extensions.html

https://dev.mysql.com/doc/refman/5.6/en/index-extensions.html

http://blog.51cto.com/huanghualiang/1557306

http://reckey.iteye.com/blog/2258450

相关文章
相关标签/搜索