「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」
聚簇索引(主键索引)
在不停的插入数据的时候,会增加一个个数据页,如果主键不是自增,那么就会在数据页之间进行数据行移动,保证下一页的主键值都大于上一个数据页的主键值。在MySQL中为了方便主键的查找,采用B+树这种树形结构来建立主键索引。如图所示:
当把索引页和数据页综合一起来看的时候,他们都是连接在一起,就如同一颗完整大的B+树一样,一直到所有的数据页,其实就组成了一颗巨大的B+树,在B+树里,最底的一层就是数据页,数据页也就是B+树里的叶子节点,可以称这颗B+树索引为聚簇索引。 在InnoDB存储引擎里,对数据增删改操作时候,会直接把数据页放到聚簇索引里,当插入数据的时候,就是在数据页里插入数据,当数据页进行分裂的时候,会调整各个数据页内部的行数据,保证数据页的主键值都是有顺序的,下一页的所有键值大于上一个数据页的所有主键值。同时页分裂的时候,会维护上层索引数据结构,在上层索引页里维护索引条目,不同的数据页和最小主键值。 如果数据页越来越多,一个索引页放不下,这时候就会拉取新的索引页,同时在搞个上层的索引页,上层索引页存放索引条目就是下层索引页页号和最小主键值,随着数据量的增大,可能会多出更多索引页层级来,一般索引页里可以放很多索引条目,通常而言,亿级的大表,基本上建的索引层级也就是三四层。 聚簇索引默认是按照主键来组织,当在增删改的时候,一方面会更新数据页,一方面也会自动维护B+树结构的聚簇索引。
二级索引
对主键系统会自动建立聚簇索引,那么针对其他非主键的字段建立索引,也会采用B+树来建立,这时候插入数据的时候,一方面会把完整的数据插入到聚簇索引的叶子节点的数据页去,同时维护聚簇索引,当有其他字段建立的索引,会重新再建立一颗B+数,也会随着数据的增删改操作而维护。 例如基于name,age字段建立的索引,那么插入数据的时候,会重新建立一个B+树,B+树的叶子节点也是数据页,但是这个数据页里仅仅存放主键字段,name字段,age字段,这颗树与聚簇索引其他都是一样,唯一不同的是叶子节点存放的数据字段不同,这种非聚簇索引称为:二级索引。 执行下面的语句:SELECT * FROM t_user WHERE name = 'xxx';首先会根据二级索引(name+age字段索引)来找到具体到的叶子节点,在叶子节点带有主键,name字段,age字段,因为查询的列是所有,此时就会根据主键去聚簇索引找到这行数据的所有字段,这种还需要进行根据聚簇索引查找行数据的操作称为回表。 相反如果执行这样的语句:SELECT name,age FROM t_user WHERE name = 'xxx';那么就不会进行回表操作,因为通过二级索引找到的叶子节点,已经带有name和age字段,就不需要在回表查询其他字段,这种情况:需要的字段可以直接在索引数里能够查询出来,不要回表到聚簇索引查询,这种查询方式就是覆盖索引。 根据表里的字段建立二级索引,可以直接根据某个字段的索引B+树来查找数据,不需要全表扫描,性能提高了很多,在实际中,大表数据的查询都要基于索引来查找,如果查询的语句没有索引的话,那么就会发生全表扫描,轻者查询超时,重点可能整个MySQL服务都会挂掉。 在使用二级索引的时候也会带来一些额外的负面坏处,主要有两点:
- 在给字段建立索引的时候,没个索引都会建立一个B+数,每一颗B+数都要占据很多的磁盘空间,所以索引搞的越多,磁盘所用的空间也比较多,在先有的存储条件下,这种磁盘空间的占用并不是主要缺点,主要缺点是时间。
- 当对数据进行增删改的时候,每次都需要维护各个索引的数据有效性,因为每个B+树都要求页内是按照值大小排序,在不停的增删改的时候,索引就不会不停的维护,这个过程比较耗费时间,如果一个表的索引建的过多,那么需要维护的B+树也会维护更多,那么多整个操作都会受到影响,会增加操作的耗时。所以在设计的时候要用尽量少的索引来满足业务需求。
在整个表中,二级索引不能够太多,一方面是要占据太多的空间,二方面在进行数据修改的时候二级索引树也要维护,索引数太多维护成本也高。