Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引

Mysql为什么添加索引可以提高查询速度,看完这篇就够了

导读:本篇理论知识比较多,这个问题如果你会那么请绕道,不要浪费时间,如果没有掌握那你就好好看看,有不明白的下方直接评论留言。谢谢!

问题:关于索引搜索问题,聚集索引可以直接找到数据,对于非聚集索引需要回表查询,那么select count(*) from table 是否需要回表查询呢?why?(文章最后解答)

数据库两大神器:索引+锁,上篇中我们将了mysql的索引原理以及算法(如果不清楚的一定要看看,对理解这篇文章有帮助),这篇我们主要聊聊B+树数据结构的索引。

数据库中B+树索引的高度一般都是2~4层,所以我们在检索一条数据的时候也只需要2~4次的IO即可。

如果数据表中没有建立索引,那么我们在检索数据的时候会造成全表扫描,不仅仅会加大服务器压力而且效率还很低下。这也就引出了我们为什么要添加索引。

在B+树数据结构中数据是如何检索出来的

检索原理:B+树索引不能找到一个给定key的具体value,B+树索引能找到的只是被查找的key所在的page。然后数据库在把找到的这个page读取到缓冲池(buffer poll)中,然后在内存中通过二分查找法进行查找对应的value。

在Mysql中,有两大常用的存储引擎MyisAm和Innodb。(这些内容经常面试常问)

Myisam使用的是非聚集索引也叫非聚簇索引InnoDb使用的是聚集索引

简单概况一下:

聚集索引就是以主键创建的索引非聚集索引就是除了主键以外的索引。非聚集索引也叫做二级索引,不用纠结那么多名词,将其等价就行了。非聚集索引在建立的时候也未必是单列的,可以多个列来创建索引。本质区别:

表记录的排列顺序和索引的排列顺序是否一致。

聚集索引(clustered)也叫聚簇索引

定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。

聚集索引表记录的排列顺序和索引的排列顺序保持一致,所以查询效率相当快。只要找到第一个索引记录的值,其余的连续性的记录也一定是连续存放的。聚集索引的缺点就是修改起来比较版,因为它需要保持表中记录和索引的顺序需要一致,在插入新记录的时候就会对数据也重新做一次排序。在数据表创建上INNODB聚集索引存储的是一个文件,后缀为.frmInnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。非聚集索引(unclustered)

定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。

非聚集索引定义了表中记录的一些逻辑顺序,但记录的物理和索引不一定保持一致,两种索引都采用B+树的结构,非聚集索引的叶子层并不喝世纪数据叶相互重叠,而是采用叶子层包含一个指向表中的记录指针。非聚集索引的缺点就是索引的层次比较多,但是不会造成数据的重排。在数据表创建上Myisam存储为三个文件.frm、.MYD、.MYIMyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。NOTE:用新华字典比喻一下,聚集索引可以说是拼音排序索引,而非聚集索引就是笔画排序索引,索引和物理顺序并不是按一定顺序存放的。文件后缀名解释.frm:表的定义,就是描述表结构的文件.MYD:数据存储文件.MYI:索引存储文件

下面用图具体说明一下

Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引_第1张图片

这张图中ID ——> 聚集索引 这张图中username ——> 非聚集索引

第一列表示数据在磁盘中的物理地址,后面三列才是我们SQL里面用的表里的列,其中id是主键,建立了聚集索引。

结合上面的表格就可以理解这句话了吧:数据行的物理顺序与列值的顺序相同,如果我们查询id比较靠后的数据,那么这行数据的地址在磁盘中的物理地址也会比较靠后。而且由于物理排列方式与聚集索引的顺序相同,所以也就只能建立一个聚集索引了。

其实按照定义,除了聚集索引以外的索引都是非聚集索引,只是人们想细分一下非聚集索引,分成普通索引,唯一索引,全文索引。

本节重点说INNODB聚集索引

InnoDB 在 B+ 树数据结构中索引又分为聚集索引和辅助索引

实操:创建数据表插入数据测试

创建表的时候指定主键(注意:SQL Sever默认主键为聚集索引,也可以指定为非聚集索引,而MySQL里主键就是聚集索引)

USE test; /* 使用test这个database */DROP TABLE IF EXISTS t8; /* 如果表t1存在则删除表t1 */CREATE TABLE `t8` (`id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) NOT NULL, `b` char(2) NOT NULL, PRIMARY KEY (`id`), /*聚集索引*/ KEY `idx_a` (`a`) /*辅助索引*/) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into t8(a,b) values (1,'a'),(2,'b'),(3,'c'),(5,'e'),(6,'f'),(7,'g'),(9,'i');

最好还是在创建表的时候添加聚集索引,由于聚集索引的物理顺序上的特殊性,因此如果再在上面创建索引的时候会根据索引列的排序移动全部数据行上面的顺序,会非常地耗费时间以及性能。

前面我说过InnoDB 的数据是按照主键顺序存放的,而聚集索引就是按照每张表的主键构造一颗 B+ 树,它的叶子节点存放的是整行数据。

InnoDB 的主键一定是聚集索引。如果没有定义主键,聚集索引可能是第一个不允许为 null 的唯一索引,也有可能是 row id。

由于实际的数据页只能按照一颗 B+ 树进行排序,因此每张表只能有一个聚集索引(TokuDB 引擎除外)。查询优化器倾向于采用聚集索引,因为聚集索引能够在 B+ 树索引的叶子节点上直接找到数据。

聚集索引对于主键的排序查找和范围查找速度非常快。

对于刚刚创建好的测试表 t8,我们先查询下表的所有数据:

Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引_第2张图片

 

表 t8 的聚集索引的大致结构如下:

Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引_第3张图片

 

聚集索引两点关键信息:

根据主键值创建了 B+ 树结构每个叶子节点包含了整行数据INNODB 辅助索引

我们现在知道了聚集索引的叶子节点存放了整行数据,而 InnoDB 存储引擎辅助索引的叶子节点并不会放整行数据,而存放的是键值和主键 ID。

当通过辅助索引来寻找数据时,InnoDB 存储引擎会遍历辅助索引树查找到对应记录的主键,然后通过主键索引来找到对应的行数据。

比如一颗高度为 3 的辅助索引树中查找数据,那需要对这颗辅助索引树遍历 3 次找到指定主键,如果聚集索引树的高度也为 3,那么还需要对聚集索引树进行 3 次查找,最终找到一个完整的行数据所在的页,因此获取数据一共需要6次逻辑 IO 访问。

我们继续拿表 t8 分析,它的辅助索引 idx_a 结构如下:

Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引_第4张图片

 

上图中两点关键点需要注意:

根据 a 字段的值创建了 B+ 树结构每个叶子节点保存的是 a 字段自己的键值和主键 ID对于表 t8,比如有下面这条查询语句:

select * from t8 where a=3;

它先通过 a 字段上的索引树,得到主键 id 为 3,再到 id 的聚集索引树上找到对应的行数据。

而下面这条 SQL:

select * from t8 where id=3;

查询到的结果是一样的,而执行过程则只需要搜索 id 的聚集索引树。我们能看出辅助索引的查询比主键查询多扫描一颗索引树,所以,我们应该尽量使用主键做为条件进行查询。

如何解决非聚集索引的二次查询问题

建立两列以上的索引,即可查询复合索引里的列的数据而不需要进行回表二次查询,如index(col1, col2)

执行下面的语句

select col1, col2 from t1 where col1 = '213';

要注意使用复合索引需要满足最左侧索引的原则,也就是查询的时候如果where条件里面没有最左边的一到多列,索引就不会起作用。

最后总结一下

使用聚集索引的查询效率要比非聚集索引的效率要高,但是如果需要频繁去改变聚集索引的值,写入性能并不高,因为需要移动对应数据的物理位置非聚集索引在查询的时候可以的话就避免二次查询,这样性能会大幅提升。不是所有的表都适合建立索引,只有数据量大表才适合建立索引,且建立在选择性高的列上面性能会更好。问题解答:count(*)是统计表数据数量的,在查询的时候虽然优先走非聚集索引,但是它不需要回表操作,它只需要统计非聚集索引树上的值即可,属于mysql5.7.18新特性!

知识点:

1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。(也称一次IO操作

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

4、B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。

你可能感兴趣的:(mysql,mysql,数据库,database)