MySQL/InnoDB - 带有 UUID 的聚类索引

use*_*625 2 mysql performance clustered-index uuid

在 MS SQL Server 中,可以在非主、非唯一键上聚集索引。例如,如果我想在插入日期创建一个聚集索引以防止在 UUID 主键中间插入时页面拆分和碎片,SQL Server 将为此执行后台工作。

在 MySQL/InnoDB 中,聚集索引将在添加到表中的第一个主键或唯一键上创建。除了添加非 UUID 主键或不构建任何唯一键之外,如何避免主要碎片?有没有其他引擎可以更好地工作?

如果不可避免,除了定期重建聚集索引之外,是否可以采取任何措施来缓解该问题?

Ric*_*mes 5

面对现实,无论您在哪里将 UUID 作为 KEY,它都会位于碎片化的 BTree 中。但是,BTree 保持相当干净。也就是说,当一个 BTree 块太满而无法接受另一行时,它会分裂成两个块,每个块大约半满。随着时间的推移,任何新插入到这些块中的任何一个都将简单地添加到块中,而不会立即拆分。“随机”插入的最终结果是平均大约 69% 已满的块。这仅比 100% 满略差。

InnoDB 仅使用 BTrees。数据使用 PRIMARY KEY 进行“聚类”。总有一个 PRIMARY KEY:

  1. 明确的 PK,或
  2. 具有非空列的第一个 UNIQUE 键,或
  3. 一个伪造的、隐藏的、6 字节的 PK。

如果您的 UUID 是 PK,那么您正在对数据进行拆分/分段。如果您的 UUID 是辅助键,那么BTree 会遭受分裂/碎片化。(几乎)没有逃脱它。

我说“几乎”是因为如果您使用的是 Type-1 UUID,您可以对这些位进行混洗,使它们大致面向时间。这使得它们很像AUTO_INCREMENTid。我在我的博客中讨论过。

是的,您可以重建包含 UUID 的任何索引。这是通过OPTIMIZE TABLE,它重建表,在此过程中阻止访问。而且,正如我所说,您不会从中获得太多收益。普通区块分裂成本不高;OPTIMIZE是。我经常告诉人们“永远”不要使用OPTIMIZE.

您的整个表是否小到可以缓存在 buffer_pool 中?如果是这样,UUID 就不是一个太大的性能问题。另一方面,一旦表(或 UUID 索引)变得比缓冲池大得多,处理就变成了 I/O 密集型。这是因为 UUID 的随机性。随着表的增长,每个 INSERT 和每个 SELECT(使用 UUID)变得越来越可能需要磁盘命中。

如果您无法避免使用 UUID(这将是我的第一个建议),您至少可以通过临时实现 ( VARCHAR(36)w/utf8) 或 36 字节 ( CHAR(36)w/ascii)将它们从 110 字节缩小到 16 字节 ( BINARY(16)) . 有关这对存储函数,请参阅我的博客。更小 --> 更可缓存 --> 更少的 I/O --> 更快。

如果您想进一步讨论您在做什么,我很乐意进一步详细说明。