摘要:InnoDB引擎对索引的扩展,自动追加主键值及其对执行计划的影响。

MySQL中,使用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表,其主键为(i1, i2),同时创建了基于d列的索引k_d,但其实在底层,InnoDB引擎将索引k_d扩展成(d,i1,i2)。

InnoDB引擎这么做,是用空间换性能,优化器在判断是否使用索引及使用哪个索引时会有更多列参考,这样可能生成更高效的执行计划,获得更好的性能。

优化器在ref、range和index_merge类型的访问,Loose Index Scan访问,连接和排序优化, MIN()/MAX()优化时使都会使用扩展列。

我们来看个例子:

root@database-one 15:15:  [gftest]> 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;
Query OK, 0 rows affected (0.06 sec)root@database-one 15:15:  [gftest]> 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');
Query OK, 25 rows affected (0.01 sec)
Records: 25  Duplicates: 0  Warnings: 0root@database-one 15:21:  [gftest]> show index from t1;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t1    |          0 | PRIMARY  |            1 | i1          | A         |           5 |     NULL | NULL   |      | BTREE      |         |               |
| t1    |          0 | PRIMARY  |            2 | i2          | A         |          25 |     NULL | NULL   |      | BTREE      |         |               |
| t1    |          1 | k_d      |            1 | d           | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.01 sec)

在普通索引中追加扩展主键是InnoDB在底层做的,show index等语句不显示追加列,但我们可以通过其它方式来验证。看这个SQL

SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = ‘2000-01-01’

如果InnoDB没有扩展索引,索引k_d为(d),生成的执行计划应该类似这样,使用k_d索引找到d为’2000-01-01’的5行数据,再回表过滤出i1为3的,最后计算count。或者使用主键索引找到i1为3的5行数据,再回表过滤出d为’2000-01-01’的,最后计算count。下面仅示意走k_d索引的情况:

mysql> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: ref
possible_keys: PRIMARY,k_dkey: k_dkey_len: 4ref: constrows: 5Extra: Using where; Using index

如果InnoDB扩展了索引,索引k_d为(d,i1,i2),这时,优化器可以使用最左边的索引前缀(d,i1),生成的执行计划应该类似这样,使用k_d索引找到d为’2000-01-01’及i1为3的1行数据,然后计算count

mysql> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: ref
possible_keys: PRIMARY,k_dkey: k_dkey_len: 8ref: const,constrows: 1Extra: Using index

并且d列是DATE类型占4个字节,i1是INT类型占4个字节,所以查询中使用的键值长度就是8个字节(key_len: 8)。

我们看看实际生成的执行计划

root@database-one 15:35:  [gftest]> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: NULLtype: ref
possible_keys: PRIMARY,k_dkey: k_dkey_len: 8ref: const,constrows: 1filtered: 100.00Extra: Using index
1 row in set, 1 warning (0.01 sec)

果然跟我们的判断一致,注意执行计划中的细节:

  • key_len从4字节变为8字节,表明键查找使用列d和i1,而不仅仅是d。

  • ref从const更改为const,const,表明查找使用两个键值,而不是一个。

  • rows从5减少到1,表明检索更少的行。

  • Extra从Using where; Using index改为Using index,表示只用索引读取,不必回表。

InnoDB引擎底层扩展普通索引的情况,也可以通过跟MyISAM引擎对比来进行旁证:

root@database-one 16:07:  [gftest]> CREATE TABLE t1MyISAM (->   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 = MyISAM;
Query OK, 0 rows affected (0.01 sec)root@database-one 16:07:  [gftest]> INSERT INTO t1myisam 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');
Query OK, 25 rows affected (0.02 sec)
Records: 25  Duplicates: 0  Warnings: 0root@database-one 16:07:  [gftest]> EXPLAIN SELECT COUNT(*) FROM t1myisam WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1myisampartitions: NULLtype: ref
possible_keys: PRIMARY,k_dkey: PRIMARYkey_len: 4ref: constrows: 4filtered: 16.00Extra: Using where
1 row in set, 1 warning (0.01 sec)

可以看到,同样的结构同样的数据,因为MyISAM引擎不会在底层自动扩展普通索引,所以执行计划还是通过主键索引进行处理。

按照官方手册的说明,也可以用SHOW STATUS命令来验证

root@database-one 16:12:  [gftest]> FLUSH TABLE t1;
Query OK, 0 rows affected (0.00 sec)root@database-one 16:12:  [gftest]> FLUSH STATUS;
Query OK, 0 rows affected (0.14 sec)root@database-one 16:12:  [gftest]> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.03 sec)root@database-one 16:12:  [gftest]> 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.01 sec)root@database-one 16:13:  [gftest]> FLUSH TABLE t1myisam;
Query OK, 0 rows affected (0.01 sec)root@database-one 16:13:  [gftest]> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)root@database-one 16:13:  [gftest]> SELECT COUNT(*) FROM t1myisam WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.01 sec)root@database-one 16:13:  [gftest]> 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)

Handler_read_next表示在进行索引扫描时,按照索引从数据文件里取数据的次数。使用MyISAM引擎的t1myisam表,Handler_read_next值为5,使用InnoDB引擎的t1表,Handler_read_next值减小到1,就是因为InnoDB引擎对索引进行了主键扩展,读取的次数少,效率更好。

默认情况下,优化器分析InnoDB表的索引时会考虑扩展列,但如果因为特殊原因让优化器不考虑扩展列,可以使用SET optimizer_switch = 'use_index_extensions=off’设置。

root@database-one 16:26:  [gftest]> SET optimizer_switch = 'use_index_extensions=off';
Query OK, 0 rows affected (0.01 sec)root@database-one 16:26:  [gftest]> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: NULLtype: ref
possible_keys: PRIMARY,k_dkey: PRIMARYkey_len: 4ref: constrows: 5filtered: 20.00Extra: Using where
1 row in set, 1 warning (0.02 sec)

墨天轮原文链接:https://www.modb.pro/db/22927

推荐阅读:144页!分享珍藏已久的数据库技术年刊


点击下图查看更多 ↓

云和恩墨大讲堂 | 一个分享交流的地方

长按,识别二维码,加入万人交流社群

请备注:云和恩墨大讲堂

  点个“在看”

你的喜欢会被看到❤

MySQL中InnoDB引擎对索引的扩展相关推荐

  1. MySQL高级部分( 二: MySQL架构、引擎、索引)

    MySQL高级 二: MySQL架构.引擎.索引.事务 MySQL架构 MySQL 的完整架构图 各层介绍 连接层 服务层 存储引擎层 Pluggable Storage Engine 物理文件存储层 ...

  2. mysql数据库存储引擎和索引的描述_Mysql InnoDB引擎的索引与存储结构详解

    前言 在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的. 而MySql数据库提供了多种存储引擎.用户可以根据不同的需求为数据表选择不同的存储引擎,用户也 ...

  3. MySQL中Innodb的索引

           如果想在一本书中找到某个特定主题,一般会先看输的"索引",找到对应的页码.在MySQL中,存储引擎用类似的方法使用索引,其先在索引中找到对应值,然后根据匹配的索引记录 ...

  4. Mysql 索引 总结 —— 概述 || 索引优势劣势|| 索引结构(索引是在MySQL的存储引擎层中实现的)|| BTREE 结构||B+TREE 结构||MySQL中的B+Tree||索引分类

    索引概述 MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构(有序). 在数据之外,数据库系统还维护者满足特定查找算法的数据结构, 这些数据结构以某种方式引用(指向 ...

  5. mysql 5.6l安装教程,Mysql中MyISAM引擎和InnoDB引擎的比较

    结论 如果不清楚自己应该用什么引擎,那么请选择InnoDB,Mysql5.5+的版本默认引擎都是InnoDB,早期的Mysql版本默认的引擎是MyISAM MyISAM 和 InnoDB的适用场景 M ...

  6. Mysql (InnoDB引擎)聚集索引和辅助索引

    聚集索引: InnoDB存储引擎表是索引组织表,即按照主键的顺序存储数据.  聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,树中的叶子节点存放着表中的行记录数据,因此, ...

  7. MySQL中MyISAM引擎与InnoDB引擎性能简单测试[转]

    MySQL中MyISAM引擎与InnoDB引擎性能简单测试 [硬件配置] CPU : AMD2500+ (1.8G) 内存: 1G/现代 硬盘: 80G/IDE [软件配置] OS : Windows ...

  8. mysql 5.7 io 性能 aio_深入理解MySQL的InnoDB引擎

    在MySQL中的引擎一文中说了,我们在几乎所有的情况下其实用的都是InnoDB引擎,这里我们就重点再看一下这个引擎,包括他的存储结构,线程模型和数据文件. 我们可以通过show engine inno ...

  9. mysql中b树索引_Mongo和Mysql中的B树索引

    为什么Mysql中Innodb的索引结构采取B+树? 回答这个问题时,给自己留一条后路,不要把B树喷的一文不值.因为网上有些答案是说,B树不适合做文件存储系统的索引结构.如果按照那种答法,自己就给自己 ...

最新文章

  1. 记录Nginx搭建网关服务
  2. 河南理工大学计算机学院地图,计算机科学与技术0812-河南理工大学计算机科学与技术学院.PDF...
  3. 波士顿动力机器人全体出动,奉上新年之舞!
  4. python中left是什么意思_Python left
  5. asp.net中打印指定控件内容
  6. 排序算法汇总(C/C++实现)
  7. C++基础知识(六)函数
  8. python中capitalize函数_python capitalize
  9. 公司内部项目章程模板
  10. html5 drag api
  11. 云计算知识3:弹性计算云EC2的基本架构
  12. AutoCAD2014官方原版软件下载
  13. java math 最大值_java 中Math 的常用方法
  14. 安卓一键清理内存_雨点清理app下载-雨点清理下载 v1.0 安卓版
  15. “九型人格”-你的团队用了吗
  16. 为什么阿里巴巴规定禁止超过三张表 join
  17. 5G知识之0G-5G的技术发展
  18. 同为PM,项目经理和产品经理那个才是未来的CEO?【大海午餐9】
  19. jquery如何根据id获取标签内的值,以及如何通过id赋值
  20. 用于通过声波捕获显示视觉,触觉和音频的全息显示(A volumetric display for visual, tactile and audio presentation using acous)

热门文章

  1. devops 三十六计_DevOps从业人员应遵循的16个博客和新闻通讯
  2. (3)Node.js APIS
  3. (8)css常用样式属性3
  4. JavaScript逻辑运算符“”和“||”短路原则的应用
  5. Bootstrap3 滚动监听插件的方法
  6. Bootstrap 弹出提示插件Popover 的方法
  7. CSS3 文本阴影 text-shadow属性
  8. java怎么检测代码安全_foritfy代码安全审计、foritfy代码检测服务、java代码安全审计检测、C/C++语言代码安全审计检测...
  9. gis怎么提取水系_SketchUp+Global Mapper 地形提取,连建模都省了...
  10. echart的关系图高亮_Echarts 环形图 默认高亮展示某个数据