MySQL索引

151 阅读7分钟

误区: 索引存放的位置在哪?磁盘还是内存? 索引和实际的数据都是存储在磁盘的,只不过在进行数据读取的时候会优先把索引加载到内存中

存储引擎: 不同的数据文件在磁盘的不同组织形式

Innodb: 生成的数据库文件为 .frm + .ibd (默认是Innodb)

MyISAM: 生成的数据库文件为 .frm + .MYD + .MYI

.frm  存放表结构
.ibd  存放数据+索引
.MYD  存放数据
.MYI  存放索引

实现MySQL索引的数据结构

hash表、树、B+树 ,最后MySQL选择的是B+树,原因如下:

1. 使用hash表的存储方式

image.png

在相同下标位置存储的数据以链表的方式连接,如果下标没有数据存储,会造成存储空间的浪费,就好比如图中的0,2,3,5,6,7,这样看起来,数据就不够的散列,不像hash表的结构特点。而且,数据的存储是没有顺序的,所以我们在进行范围查询数据的时候,效率就十分的低下,也就是说:

  • 需要好的hash算法来实现索引,不然可能会出现hash碰撞,hash冲突,导致数据散列不均匀。
  • 由于数据的存储没有顺序,导致进行范围查找的时候需要一个一个查,效率低下。

注意:memory的存储引擎支持的就是hash索引,而默认的innodb存储引擎支持自适应hash

  • 自适应hash:innodb的一大特点,由mysql服务自己决定索引是使用hash表还是B+树,认为干预不了。

2. 使用树的存储方式

image.png

在这种存储方式下,当插入新的数据时,树的深度会变深,访问效率变低(IO次数变多),影响查询效率。

原因:这四种树的共同点决定的:有且仅有两个节点,所以插入数据很容易增加树的深度

因此,B树诞生了

3. B树

何为B树,B树就是上面四种树的变形,它变形的地方就在于:它的每个节点存放的数据不再只是一个了,而是由自己定义的来决定的。什么意思呢?就是说,当你定义了4时,每个节点存放的数据最多有3个(度-1),超过就得向上分裂,也就是下面这样的如图变化。

image.png

image.png

对应到MySQL的表就是

image.png

image.png

这样子存储数据比起之前那几种树的效率是增加了,但是,因为在每个磁盘块里面还是存放着data数据,所以说每个磁盘块存放的数据还是太少了,当数据很多时,也会出现和上面四种树一样的情况,访问IO的次数增多,查询效率慢。

基于这个问题,我们的主角B+树出现了

4. B+树

B+树就是B树的改良版本,不同的点在于:B+树只有它的叶子节点才存放数据,并且通过循环链表的方式连接起来,而其他非叶子节点只存放索引

image.png

对应到数据库的表结构

image.png

注意: 在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

一般情况下,3-4层的B+树足以存储千万级别的数据。

设计索引的时候,类型的选择:int 还是 varchar ?

int占用4字节,varchar占用varchar(n)中的n个字节,灵活选择~~~

聚簇索引 vs 非聚簇索引

  • 聚簇索引: 数据和索引存放在一起
  • 非聚簇索引: 数据和索引不存放在一起

注意:innodb既有聚簇索引也有非聚簇索引,而myisam只有非聚簇索引

1. innodb存储引擎在进行数据插入的时候,数据必须要跟某一个索引列存储在一起,
这个索引列可以是主键,如果没有主键,选择唯一键,如果没有唯一键,选择6字节的rowid来进行存储
2. 当你建立了不止一个索引的时候,其他索引在B+树的叶子节点存放的数据不是数据库中整行的记录,而是其前一个聚簇索引的id值
3. 第二点就可以体现innodb存在非聚簇索引,因为其他索引没有和其前一个聚簇索引的id值,这样来看(其他索引就没有绑定数据,也就不是聚簇索引了)

再来一个例子说明一下吧:比如表中有id,name,age,sex这四个属性,id为主键,name为普通索引。

此时,id是聚簇索引,因为它是主键,和数据绑定在一起,name对应的索引在B+树的叶子节点存放的就是id值,
name对应的索引就是非聚簇索引。

也有一种说法是:非聚簇索引就是辅助索引,或者是二级索引。

既然说到了这个,不妨再来看看下面这四个名词:

  • 回表
  • 索引覆盖
  • 最左匹配
  • 索引下推 如果你知道,直接跳过,不知道的就看一下

1. 回表:要尽量避免

直接上例子,比较容易懂

有一张表:id,name,age,sex
主键是id,name是普通索引
当你进行查询时:
select * from table where name="张三"; ==> 出现回表
解释:
SQL语句的执行过程:先根据name的B+树匹配叶子节点,再根据叶子节点存储的id值,去id的B+树查找出其他数据
这样就称之为回表,因为它根据id值去另外一颗B+树进行查询数据

2. 索引覆盖

老规矩,直接实例分析

有一张表:id,name,age,sex
主键是id,name是普通索引
select id,name from table where name="张三";
解释:
SQL语句的执行过程:根据name的值去name的B+树检索对应的记录,能获取到id的属性值,索引的叶子节点中包含了查询的所有列,此时不需要回表,这个过程叫做索引覆盖,using index的提示信息,推荐使用,在某些场景中,可以考虑将要查询的所有列都变成组合索引,此时会使用索引覆盖,加快查询效率

2. 最左匹配

创建索引的时候可以选择多个列来共同组成索引,此时叫做组合索引或者联合索引,要遵循最左匹配原则,直接上案例

有一张表:id,name,age,sex
id主键,<name,age>组合索引
1. select * from table where name="张三" and age =12;  遵循最左匹配原则
2. select * from table where name="张三" ;             遵循最左匹配原则
3. select * from table where age =12;                不遵循最左匹配原则
4. select * from table where age =12 and name="张三";  遵循最左匹配原则
解释:
因为是<name,age>的组合索引,所以先匹配name,再匹配age,语句4遵循的原因是因为mysql自带where条件语句的优化查询,mysql先将语句4优化成了语句1的形式。

2. 索引下推

有一张表:id,name,age,sex
id主键
select * from table where name="张三" and age=12;
没有索引下推之前:
先根据name从存储引擎中垃取数据到server层,然后在server层中对age进行数据过滤
有了索引下推之后:
根据name和age两个条件来做数据筛选,将筛选之后的结果返回给server层
此时的索引下推就相当于把原本在server层对age的过滤放到存储引擎中,也就是先在存储引擎中进行数据筛选。