MySql性能(3)—InnoDB中的聚簇索引和普通索引

1. 什么叫做聚簇索引

mysql的InnoDB底层是采取B+Tree实现,在叶子节点的data域中存储的是实际的数据行。因为无法将数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。在InnoDB中,通常选择主键作为聚簇索引。

2. InnoDB选取主键

如果没有定义主键,InnoDB会选择一个唯一非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键作为聚簇索引。

3. 聚簇索引的优点

  • 将相关数据保存在一起。例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户的全部邮件。如果没有聚簇索引,则每一封邮件都可能导致一次IO。
  • 访问数据更快。聚簇索引将索引和数据保存在同一节点中,因此从聚簇索引中获取数据通常比非聚簇索引中查找更快。
  • 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。

4. 聚簇索引的缺点

  • 聚簇索引可以提高IO密集型应用的性能,但是如果数据全部在内存中,聚簇索引也就没有优势了。
  • 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到InooDB表中速度最快的方式;
  • 更新聚簇索引列的代价很高,因为会强制InooDB将每个被更新的行移动到新的位置。
  • 基于聚簇索引的在插入新行,或者主键被更新导致需要移动行的时候,可能会面临“节点分裂(页分裂)”的问题。而发生页分裂会导致表占用更多的磁盘空间。
  • 聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏、或者由于页分裂导致数据存储不连续的时候。
  • 二级索引(非聚簇索引)可能比想象中的要变大,因为二级索引的叶子节点包含了引用行的主键列;
  • 二级索引访问需要两次索引查找,而不是一次。

5. 二级索引(普通索引)

二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值。这意味着通过二级索引查找行,存储引擎需要找到二级索引的叶子节点获得对应的主键值,然后根据这个值去聚簇索引中查找到对应的行。这里做了重复的工作。

InnoDB表是基于聚簇索引建立的,InnoDB的索引结构和MySQL的其他存储引擎有很大的不同,聚簇索引对主键查询有很高的性能。不过它的二级索引(非主键索引)中必须包含主键列,所以如果主键列很大的话,其他索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。

二级索引为什么存储的是主键值而不是行指针?

InnoDB二级索引的叶子节点存储的不是“行指针”,而是主键值,并以此作为指向行的“指针”。这样的策略减少了当出现行移动或者数据列分裂时二级索引的维护工作。使用主键值当做指针会让二级索引占用更多的空间,换来的好处是InnoDB在移动行时无需更新二级索引中的这个指针。

MySql性能(3)—InnoDB中的聚簇索引和普通索引_第1张图片
InnoDB和MyISAM的二级索引区别.png

如果正在使用InnoDB表并且没有什么数据需要聚集,那么可以定义一个代理键作为主键,这种主键的数据应该和应用无关,最简单的方法就是使用AUTO_INCREMENT自增列。这样就可以保证数据行是按顺序写入,对于根据主键做关联查询的性能会更好。

6. 为什么选择自增作为主键

最好避免随机的(不连续且值的分布范围非常大)聚簇索引,特别是对于IO密集型的应用。例如,从性能的角度考虑,使用UUID来做为聚簇索引则会很糟糕:它使得聚簇索引的插入变得完全随机,这是最坏的情况,使得数据没有任何的聚集特性。

当主键的值是顺序的,InnoDB把每一条记录都存储在上一条记录的后面。当达到页的最大填充因子时(InnoDB默认的最大填充因子时页大小的15/16,留出部分空间用于以后偶的修改),下一条记录就会写入新的页中。一旦数据按照这种顺序加载,主键页就会近似于被顺序的记录填满,这也是所期望的结果。

而使用UUID作为主键填充,因为新行主键值不一定比之前插入的大,所以InnoDB无法简单地总是把新行插入到索引的最后,而是需要为新行寻找合适的位置——通常是已有数据的中间位置——并且分配空间。这会增加很多额外工作,并导致数据分布不够优化。

  • 被写满且已经刷新到磁盘上的页可能会被重新读取。会造成大量的IO操作。

  • 写入是乱序的,InnoDB不得不频繁地做页分裂操作,以便为新的行分配空间。页分裂会导致移动大量数据,一次插入最少需要修改三个页而不是一个页。

  • 由于频繁的页分裂,页会变得稀疏并被不规则地填充,所以最终数据会有碎片。

使用UUID作为主键不仅花费的时间更长,而且索引占用的空间也更大。这一方面是由于主键字段更长;另一方面毫无疑问是由于页分裂和碎片导致的。

顺序的主键可能存在的问题

对于高并发工作负载,在InnoDB中按主键顺序插入可能会造成明显的争用。主键的上界会成为“热点”。因为所有的插入都会发生在这里,所以并发插入可能会造成间隙锁的竞争,另一个热点可能是AUTO_INCRMENT机制;
如果遇到这个问题,则可能需要重新设计表或应用,或者更改innodb_autoinc_lock_mode的参数。

推荐阅读

mysql间隙锁产生原因

历史文章

mybatis&&数据库优化&&缓存目录
JAVA && Spring && SpringBoot2.x — 学习目录

你可能感兴趣的:(MySql性能(3)—InnoDB中的聚簇索引和普通索引)