主键聚簇索引与主键非聚簇索引

主键聚簇索引和主键非聚簇索引,两者在效率上有差别吗?
如果有差别,或者说主键聚簇索引效率更高一些,那么从最佳实践的角度,在创建表的时候强烈建议指定主键,并且主键的类型属于INTEGER系列呢?

主键聚簇索引数据是和主键一起的,相对非聚簇索引少了一次寻址,检索性能会高一点

tidb底层是key:value结构,如果是主键聚簇索引的话,表的key就是tablePrefix{ TablelD }_recordPrefixSep{ 主键 },这样按主键查询的话,单个值就走点查,tidb叫Point_Get,多个值就走TableRangeScan,不需要再走索引后回表了,直接生成key,去tikv将对应的value拿回来,就是需要的数据了。
如果主键非聚簇索引,tidb的key会自动生成一个虚拟列_Tidb_RowlD,他的key是tablePrefix { TablelD }_recordPrefixSep{ _Tidb_RowlD },建主键的时候会重新创建一个索引,存放索引值和key这样,你通过主键查询的时候,需要先通过主键索引值获取到key值,然后通过key值,再回表查询,相对与上面就多了一步,效率会有点差别的。
当然主键聚簇索引也不是没有缺点,如果你的主键是数字型,并且是递增的话,就会产生热点。。需要通过其他的方式进行优化了。。

在TiDB中,聚簇主索引和非聚簇主索引有以下区别:

  1. 数据存储方式
  • 聚簇主索引:数据按照主键的顺序存储。每一行数据只需要一个键值对,即“主键数据(键)- 行数据(值)”。这种方式可以提高涉及主键的查询性能,尤其是范围查询和前缀条件查询。
  • 非聚簇主索引:数据不按照主键的顺序存储。每一行数据需要至少两个键值对:一个是“_tidb_rowid(键)- 行数据(值)”,另一个是“主键数据(键)- _tidb_rowid(值)”。这种方式在查询时需要额外的步骤来定位数据。
  1. 性能差异
  • 聚簇主索引:在插入数据时减少了一次网络写入;在仅涉及主键的等值查询中减少了一次网络读取;在仅涉及主键的范围查询中减少了多次网络读取。
  • 非聚簇主索引:由于需要额外的步骤来定位数据,性能相对较低。
  1. 潜在问题
  • 聚簇主索引:可能会在插入大量相近值的主键时导致写入热点问题。此外,如果主键的数据类型大于64位,表数据会占用更多的存储空间。
  • 非聚簇主索引:由于需要额外的步骤来定位数据,可能会导致查询性能下降。
  1. 使用场景
  • 聚簇主索引适用于需要高效查询主键的场景。
  • 非聚簇主索引适用于不需要频繁查询主键的场景。

更多详细信息可以参考以下链接:聚簇索引

这块还真亲身测试过,性能上差异不大【对于点查询,简单的范围查询场景】,但是存储容量上聚簇索引占用的空间会膨胀10%左右【特别是联合主键&整型长度超过64位后】

写入性能差距很大,聚簇表比非聚簇表快1倍
主键不同于mysql,不是非要优先用int ,用字符串,日期联合主键都可以

在一般场景下主键聚簇索引在查询、插入效率上通常优于主键非聚簇索引
针对主键的类型推荐使用 INTEGERBIGINT 类型的主键。这些类型具有以下优点:
性能高效:整数类型的索引查找和比较操作速度快,适合频繁的查询和索引操作。
存储空间小:整数类型占用的存储空间较小,可以减少索引的大小,提高索引的缓存效率。
易于管理:整数类型的主键易于生成和管理,适合自增主键等场景。

  • 数据访问方式
    • 主键聚簇索引:聚簇索引决定了数据在磁盘上的物理存储顺序,其叶子节点存储的不仅是索引列的值,还存储整行数据。通过聚簇索引进行查询时,可以直接从索引中找到所需的数据,而不需要额外的查找步骤,因此数据访问更快
    • 主键非聚簇索引:非聚簇索引的索引和数据是分开存储的,叶子节点存储的是索引列的值和指向实际数据的指针(在InnoDB中,这个指针实际上是该行对应的主键值)。通过非聚簇索引查询数据时,数据库首先需要使用非聚簇索引找到主键,然后再通过主键去聚簇索引中找到实际的数据,这个过程被称为“回表”,相比聚簇索引多了一次查找操作,效率相对较低 。
  • 查询性能
    • 主键聚簇索引:由于数据存储在索引的叶子节点中,按主键或按聚簇索引进行查询非常快,特别是范围查询(如BETWEENORDER BY)效率高。
    • 主键非聚簇索引:可以加速对索引列的查找,尤其是对那些经常作为查询条件的列。但查询时需要通过索引列找到主键,然后再去聚簇索引查找实际数据,因此比直接使用聚簇索引略慢 。

但是,我说一下主观感受:在TiDB 实现的主键聚簇索引,各种各样的限制会比MySQL大的多。这也导致我们经常遇到MySQL2tidb的同步异常,因此tidb不支持对聚簇索引类型的主键做修改。