MySQL 使用 varchar(255) UUID 进行查询和插入优化

MySQL query and insertion optimisation with varchar(255) UUIDs

我认为这个问题已经以某种形式或形式被问到,但我找不到一个问题来确切地问我想了解的内容,所以我想我应该把这个问题放在这里

问题陈述

我已经构建了一个 Web 应用程序,其中包含一个 MySQL 客户记录数据库,其中包含一个 INT(11) id PK AI 字段和一个 VARCHAR(255) uuid 字段。 uuid 字段未编入索引也未设置为唯一。 uuid 字段用作 public 标识符,因此它是 URL 等的一部分 - 例如https://web.com/get_customer/[uuid]。这样做是因为 UUID 是 'harder' 来猜测一个普通的 John Doe - 但要明白它在理论上肯定不是 'unguessable'。但现在的问题是,随着数据库越来越大,我发现检索特定客户记录的查询需要更长的时间才能完成。

我对如何解决问题的想法

想到的解决方案是使 uuid 字段唯一且索引相同。但是我一直在阅读与此和各种博客相关的文章 posts,Whosebug 对此的回答描述了将索引放在 UUID 上对性能非常不利。我还读到它还会增加将新客户记录插入数据库所需的时间,因为 MySQL 数据库需要时间来找到将记录作为索引的一部分放置的正确位置。

上面提到的 https://web.com/get_customer/[uuid] 无需身份验证即可访问,这就是为什么我不使用 id 字段的原因。我可以考虑转向基于整数的 UUID(我不需要 UUID 是普遍唯一的——它们只需要对于特定的 table 是唯一的)——这会提高索引性能吗?提高插入和查询性能?

有没有好的博客 post 或信息页面介绍如何最好地为此类需求设置数据库 - 需要能够存储 'hard' 容易猜测的客户记录便于在大数据集中插入和查询。

非常感谢任何帮助。谢谢!

你提到的关于在 UUID 上放置索引的公认智慧只有在你使用它们代替自动递增主键时才会出现。为什么?整个 table (InnoDB) 作为 clustered index 构建在主键后面,当索引值是连续的时,批量加载效果最好。

您当然可以在您的 UUID 列上放置一个普通索引。如果您希望您的 INSERT 操作在天文学上不太可能发生的事件中失败,您会得到一个随机重复的 UUID 值,您可以使用这样的索引。

ALTER TABLE customer ADD UNIQUE INDEX uuid_constraint (uuid);

但重复的 UUIDv4 确实非常罕见。 They have 122 random bits,如今大多数生成它们的软件都使用加密质量的随机数生成器。我认为,省略 UNIQUE 索引是一种 acceptable 风险。 (不要使用 UUIDv1、2、3 或 5:它们不够难猜,无法保证您的数据安全。)

如果您的 UUID 索引不是唯一的,则可以节省 INSERT 和 UPDATE 的时间:它们不需要查看索引来检测唯一性约束违规。

编辑。当 UUID 数据在 UNIQUE 索引中时,INSERT 比它们在类似的非唯一索引中的成本更高。你应该使用 UNIQUE 索引吗?如果您有大量 INSERT,则不会。如果您的 INSERT 数量较少,则可以使用 UNIQUE。

这是在您省略 UNIQUE 时使用的索引:

ALTER TABLE customer ADD UNIQUE INDEX uuid (uuid);

要使查找速度非常快,您可以使用 covering indexes。例如,如果您最常见的查找查询是

SELECT uuid, givenname, surname, email
  FROM customer
 WHERE uuid = :uuid

你可以创建这个所谓的覆盖索引。

ALTER TABLE customer 
  ADD INDEX uuid_covering (uuid, givenname, surname, email);

那么您的查询将直接从索引中得到满足,因此速度会更快。

当您有更多索引时,INSERT 和 UPDATE 操作总是会产生额外成本。但是 full table scan 的查询成本,在大的 table 中, 远远大于 额外的 INSERT 或 UPDATE 成本。如果您进行大量查询,那就更是如此了。

在计算机科学中,通常存在 space / 时间权衡。 SQL 索引使用 space 来节省时间。它通常被认为是一个很好的权衡。

(通过使用复合主键来加快速度,您可以使用各种技巧。但这是当您有 gigarows 时的主题。)

(您还可以通过将 UUID 存储在 BINARY(16) 列中并使用 UUID_TO_BIN() and BIN_TO_UUID() 函数对其进行转换来保存索引和 table space。)