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

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),即包含主键值。因此在设计主键的时候,常见的一条设计原则是要求主键字段尽量简短,以避免二级索引过大(因为二级索引会自动补齐主键字段)。

优化器会考虑扩展二级索引的主键列,确定什么时候使用以及如何使用该索引。 这样可以产生更高效的执行计划和达到更好的性能。有不少博客介绍索引扩展是从MySQL5.6.9开始引入的。不过个人还没有在官方文档看到相关资料。

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

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

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开启、关闭此选项。

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> 

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

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

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 where”与”Using 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

MySQL索引扩展(Index Extensions)学习总结的更多相关文章

  1. MySQL索引的Index method中btree和hash的优缺点

    MySQL索引的Index method中btree和hash的区别 在MySQL中,大多数索引(如 PRIMARY KEY,UNIQUE,INDEX和FULLTEXT)都是在BTREE中存储,但使用 ...

  2. MySQL索引与Index Condition Pushdown

    实际上,这个页面所讲述的是在MariaDB 5.3.3(MySQL是在5.6)开始引入的一种叫做Index Condition Pushdown(以下简称ICP)的查询优化方式.由于本身不是一个层面的 ...

  3. MySQL索引与Index Condition Pushdown(employees示例)

    实验 先从一个简单的实验开始直观认识ICP的作用. 安装数据库 首先需要安装一个支持ICP的MariaDB或MySQL数据库.我使用的是MariaDB 5.5.34,如果是使用MySQL则需要5.6版 ...

  4. MySQL索引与Index Condition Pushdown(二)

    实验 先从一个简单的实验开始直观认识ICP的作用. 安装数据库 首先需要安装一个支持ICP的MariaDB或MySQL数据库.我使用的是MariaDB 5.5.34,如果是使用MySQL则需要5.6版 ...

  5. MySQL—索引(Index)

    前言: 关于MySql索引数据结构和实现原理的讲解值得阅读一下: 实现原理:https://www.cnblogs.com/songwenjie/p/9415016.htm 索引数据结构:https: ...

  6. 8.2.1.7 Use of Index Extensions 使用索引扩展

    8.2.1.7 Use of Index Extensions 使用索引扩展 InnoDB 自动扩展每个secondary index 通过添加primary key columns to it,考虑 ...

  7. 重新学习MySQL数据库5:根据MySQL索引原理进行分析与优化

    重新学习MySQL数据库5:根据MySQL索引原理进行分析与优化 一:Mysql原理与慢查询 MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能 ...

  8. 重新学习MySQL数据库4:Mysql索引实现原理

    重新学习Mysql数据库4:Mysql索引实现原理 MySQL索引类型 (https://www.cnblogs.com/luyucheng/p/6289714.html) 一.简介 MySQL目前主 ...

  9. MySQL学习(4)---MySQL索引

    ps:没有特殊说明,此随笔中默认采用innoDB存储引擎中的索引,且索引都是指B+树(多路平衡搜索树)结构组织的索引.其中聚集索引.复合索引.前缀索引.唯一索引默认都是使用B+树,统称为索引. 索引概 ...

随机推荐

  1. MySQL及其图形界面navicat的安装

    数据库的第一步就是安装MySQL了,基本的安装方法都在这里,所以这里小编整理出来,一方面为了自己复习,一方面也方便大家. (关于MySQL的笔试题,具体见下面博客:http://www.cnblogs ...

  2. javaScript之分支判断与内置对象

    一,分支结构 单一选择结构(if) 二路选择结构(if/else) 内联三元运算符 ?: 多路选择结构(switch) 1.1  if 控制语句 if-else基本格式: if (表达式){ 语句1; ...

  3. Java——static关键字

    前言 static关键字算是Java中比较复杂的关键字之一,它可以修饰变量.方法.类以及代码块.下面将介绍static的具体使用. static引入的目的 static的作用 static修饰变量 s ...

  4. ife 零基础学院 day 1 - 我为什么想学前端

    与前端结缘   我是后端研发,毕业四年,用了四年C#,一开始写ASP.NET,有时会在asp页面写简单的js和html,做点css样式调整.当时的感触是前端调试太费劲了,因为没有js.html.css ...

  5. ResourceOwnerPassword模式使用数据库.

    有时候, ResourceOwnerPassword模式有用的, 可以用来代替我们原来管理程序的开发方式. 因为管理程序本身拥有用户数据的权限嘛, 并不是第三方应用, 无需要授权 集成很简单. 1. ...

  6. glassfish SEC5054: Certificate has expired

    出现这个错误的原因是某一个证书过期了 解决方法 先备份 cp /root/glassfish3/glassfish/domains/domain1/config/cacerts.jks cacerts ...

  7. python使用gevent实现并发下载器

    并发下载原理 import gevent from gevent import monkey import urllib.request monkey.patch_all() def my_downl ...

  8. JSP使用过滤器防止SQL注入

    什么是SQL注入攻击?引用百度百科的解释: sql注入_百度百科: 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具 ...

  9. adb命令笔记

    adb devices [-l]: 列出所有连接设备 l: 列出设备限定符 adb connect <host>[:<port>]: 通过ip连接到设备 host: IP po ...

  10. Java基础-一文搞懂位运算

    在日常的Java开发中,位运算使用的不多,使用的更多的是算数运算(+.-.*./.%).关系运算(<.>.<=.>=.==.!=)和逻辑运算(&&.||.!), ...