聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。

当表有聚簇索引时,它的数据行实际上存放在索引的叶子页(leaf page)中。术语“聚簇”表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引

下图展示了聚簇索引中的记录是如何存放的。注意,叶子页包含了一条记录的全部数据,但是节点页只包含了索引列。在这个案例中,索引列包含的是整数值。

InnoDB 根据主键聚簇数据,这意味着图中所示的“索引列”就是主键列。

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

这样做的缺点在于,所有需要使用这种隐藏主键的表都依赖一个单点的“自增值”,这可能会导致非常高的锁竞争,从而出现性能问题。

按照主键的顺序插入行是将数据加载到 InnoDB 表中最快的方式

如果不是按照主键的顺序加载数据,那么在加载完成后最好使用 OPTIMIZE TABLE 命令重新组织一下表。

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

但这也意味着通过二级索引查找行,存储引擎需要找到二级索引的叶子节点,以获得对应的主键值,然后根据这个值去聚簇索引中查找对应的行。这里做了双倍工作:两次 B-tree 查找而不是一次。

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

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

总之,使用 InnoDB 时应该尽可能地按主键顺序插入数据,并且尽可能地按照单调增加的聚簇键的值顺序插入新记录。

什么时候按主键顺序插入反而会更糟?

对于高并发的工作负载,在 InnoDB 中按主键顺序插入可能会造成明显的写入竞争

  • 主键的上界会成为“热点”。因为所有的插入都发生在这里,所以并发插入可能导致间隙锁竞争。

  • 另一个热点可能是 AUTO_INCREMENT 锁机制。

如果遇到这个问题,则可能需要考虑重新设计表或者应用,或者更改 innodb_autoinc_lock_mode 配置。

最后更新于