我正在将约 50M 行导入 MySQL 8、InnoDB。它位于具有 GP3 存储的 AWS RDS 上。
行的唯一键是类似 uuid 的字符串。
查询时,我们永远不会关心这个唯一键,除非从主源更新插入新的/修改的行。
通常,唯一的 id 就是 PK。但我读到 MySQL 中的 PK 索引很特别,因为它旨在将相似值的数据“聚集”,以提高性能。
看起来,通过使用类似于 uuid 的字符串作为 PK,聚类不会对我们的查询有帮助。
如果我要对表进行分区,我会按日期范围进行分区。
我可以想象定义一个合成 PK 或复合 PK,它将日期字段和 uuid 结合起来以获得更有可能支持我们实际执行的查询的集群。
我的问题是:拥有一个支持典型查询(即获取的结果可能在索引中“接近”)的 PK 集群有多重要?
据推测,PK 的自动递增 id 的典型情况也会导致与典型查询关系不大的聚类(通常没有理由选择相邻的 id)。
我正在特别思考现代 SSD 存储是否使这种类型的优化变得不那么重要、过时......甚至更重要?
更多背景信息
https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
聚集索引如何加速查询
通过聚集索引访问行的速度很快,因为索引搜索直接指向包含行数据的页面。如果表很大,则与使用与索引记录不同的页存储行数据的存储组织相比,聚集索引架构通常可以节省磁盘 I/O 操作。
看来 PK 索引的“聚集”性仅对于通过 PK 选择的查询有价值。
这是关于将行数据与索引(?)
因此,如果我关心的所有应用程序查询都使用二级索引,我想 PK 的属性是什么并不重要?例如,在 PK 中包含日期分区列不会神奇地加快使用不同索引的查询速度。
是对的吗?
这取决于。
对于通过 PK 的“点查询”,有 1 个 BTree 查找。
对于通过二级索引的“点查询”,有两个 BTree 查找。
对于“范围查询”,UUID 实际上毫无用处——行将分散在各处。(但是,请参阅UUID。)
对于按时间顺序聚集的信息...如果您有新闻文章,并且大多数请求都是针对“最近”的文章,那么按时间顺序对数据进行排序将是有益的。
AUTO_INCREMENT
(大部分)实现了这一点;UUID 没有(再次参见上面的链接)如果您的外部提供商为您提供了 UUID,并在稍后引用它们,那么请务必使用 UUID 作为 PK。将 UUID 转换为 auto_inc id 的开销可能不值得。
如果整个表适合
innodb_buffer_pool_size
,那么生产服务器最终将获得所有块(每个块 16KB),最终将缓存在 RAM 中。在这种情况下,PK 是 auto_inc 还是 UUID 或者其他什么都没有关系。在一些罕见的应用中(2D索引有用)
PARTITIONing
可能有用。(但我在问题中没有看到这一点。)将 UUID 打包到
BINARY(16)
(请参阅 MySQL 和 MariaDB 中现在可用的链接或函数)。(节省空间会稍微减少 I/O,从而提高性能。)一般的指数是多少?Wу 指定一些表达式。该表达式值是针对行集中的每一行计算的。然后对这些值(以及对生成该值的行的引用)进行排序。现在,当我们知道表达式的值时,我们可以使用半除法轻松找到表达式具有所需值的行。
InnoDB中什么是二级索引和非聚集索引?服务器计算每一行的索引表达式,对结果进行排序并将结果保存到单独的磁盘结构的索引中。现在,当我们指定索引表达式值时,服务器在索引中搜索该值,然后获取对该行本身的引用(该引用存在于磁盘上的表体文件中),并使用该引用检索该行本身。
什么是聚集索引?服务器计算每一行的索引表达式,根据该表达式值对行进行排序,并将行按此顺序保存到磁盘。现在,当我们指定索引表达式值时,服务器会在索引中搜索该值,当找到该索引值时,它也会找到该行本身。我们不需要跳到表体 - 我们已经在其中了。
再次关于二级索引。我已经说过,其中的索引表达式值伴随着行本身的引用。该引用应该有助于尽快找到行本身 - 因此该引用是聚集索引表达式值。即,当我们通过二级索引搜索时,我们通过其表达式值使用二分搜索,根据聚集索引表达式值,并通过该聚集表达式值使用二分搜索来查找行本身。
最后。当我们通过二级索引搜索行时,我们需要执行两次搜索。当我们通过聚集索引搜索时,我们只需要一次搜索。