在MySQL中,是否可以使用UUID作为主键?

2025-12-12 0 585

全局唯一标识符(UUID ),是一种允许开发者生成全局唯一ID的方法,其设计确保了无需了解其他系统即可保证ID的独特性。

在分布式架构中,多个系统和数据库负责记录的创建,UUID格外有用。或许,你可能认为将UUID用作数据库主键是一个不错的主意,但如果使用不当,它可能会严重影响数据库性能。

在本文中,我们将详细剖析在MySQL数据库中使用UUID作为主键的弊端,在这个过程中也详细的了解一下UUID多个版本构成。

UUID 的多个版本

截至目前,UUID有五个正式版本与三个提议版本。让我们来看每个版本的工作原理以更好地理解它们的不同。

UUIDv1

UUID 版本 1 是基于时间的 UUID,其结构如下:

UUIDv1 结构图

尽管现代计算大多使用 UNIX 时间戳 (从 1970 年 1 月 1 日开始) 作为基础,UUID 实际上使用了另一个时间起点 —— 1568 年 10 月 10 日,这一天是格里高利历 开始被广泛使用的时间。

UUID 中嵌入的时间戳从这一日期起每 100 纳秒递增,用于设置 time_lowtime_midtime_hi 部分。

UUID 的第三部分包含了版本号以及 time_hi,且该部分的第一个字符始终标志着该 UUID 的版本。这种结构适用于所有版本的 UUID(后续示例中也会指出)。保留部分称为 UUID 的变体,它决定了 UUID 内部的位使用方式。最后一部分是 node,表示生成 UUID 的系统的唯一地址。

UUIDv2

与版本 1 相比,UUID 版本 2 对结构做出了一些改变,将 low_time 部分替换为 POSIX 用户 ID。

理论上讲这种 UUID 可以追溯到生成它的用户账户。但由于 low_time 是 UUID 变动性的重要来源,替换这个部分会增加发生冲突的可能性。因此,这个版本很少被使用。

UUIDv3 和 UUIDv5

UUID 的版本 3 和版本 5 非常相似。这些版本旨在以确定性方式生成 UUID,也就是说,给定相同的信息能够生成相同的 UUID。

这些实现使用两种信息:一个命名空间(本身就是一个 UUID)和一个名称。这些值通过哈希算法生成 128 位的值并表示为 UUID。

两者的主要区别在于:版本 3 使用 MD5 哈希算法,而版本 5 使用 SHA1

UUIDv4

版本 4 被称为随机变体,因为正如其名称所示,UUID 的值几乎完全随机。不过,第一个例外是 UUID 的第三部分第一个字符,它始终是 4,用来标志 UUID 使用的是版本 4。

UUIDv4 结构图

UUIDv6

版本 6 与版本 1 几乎相同,唯一不同是记录时间戳的位顺序被翻转了,将时间戳中最重要的部分放置优先。以下图表展示了它们的区别:

UUIDv6 结构图

这么做的主要原因是生成一个与版本 1 兼容的值,同时使这些值更具排序性,因为时间戳最重要的部分被放在前面。

UUIDv7

版本 7 也是基于时间的 UUID 变体,但它使用了现代更常见的 Unix 时间戳 (而不是版本 1 中的格里高利日期)。另一个主要不同点是,将表示生成 UUID 系统的 node 替换为随机值,使 UUID 更难追溯到其来源。

UUIDv8

版本 8 是最新版本,它允许厂商特定的实现,同时遵循 RFC 标准。版本 8 的唯一要求是,版本号必须被指定在第三部分的第一个位置,这点和所有其他版本一致。

UUID和MySQL

UUID(大多数情况下)在分布式架构中保证跨系统的唯一性,因此你可能会倾向将其用作记录的主键。然而,与自增整数相比,这样做有一些显而易见的权衡。

插入性能

每当向 MySQL 表插入新记录时,与主键关联的索引需要更新以让查询变得高效。MySQL 中的索引以 B+树 的形式存在,这是一种多层数据结构,能让查询快速找到所需数据。

关于MySQL的B+树详细讲解可参考《MySQL之进阶:一篇文章搞懂MySQL索引之B+树》这篇文章。

以下图示展示了一个包含 1 到 6 的简单 B-树结构。如果一个查询要求查找值 5,MySQL 从 root 节点开始,将沿右侧路径遍历树以找到目标值。

注: 图示使用的是 B-树 而不是 B+树。两者的主要区别在于,B+树的叶节点包含对实际数据的引用,而 B-树的叶节点则不包含。

B-树结构图

当新值(例如 7 到 9)被添加时,MySQL 会拆分右侧节点并重新平衡树结构。 增加了7到9的B-树结构图

这种过程被称为 页拆分(page splitting) ,其目标是保持 B+树结构的平衡,以便 MySQL 能快速找到所需数据。对于连续值,这种过程相对简单;然而当算法中引入随机性时,MySQL 的树平衡工作可能显著变慢。在一个高流量的数据库上,这可能会影响用户体验,因为 MySQL 试图保持树结构平衡。

更高的存储利用率

MySQL 中的所有主键都是索引。默认情况下,一个自增整数的存储空间为 32 位 。相比之下,UUID 的存储不论是二进制形式还是字符串形式,其占用空间均远大于整数:

  • UUID 的二进制形式需要 128 位 ,是 32 位整数的 4 倍;
  • 字符串形式,通常为 CHAR(36),需要 288 位 ,是 32 位整数的 9 倍

此外,除主键上的默认索引外,次级索引也会占用更多空间。次级索引中使用主键作为指向实际行的指针,意味着主键也需要存储在索引中。如果表中使用 UUID 作为主键,这会显著增加数据库的存储需求,具体取决于表中创建了多少索引。

最后,随机性引发的 页拆分 (如前所述)也会对存储使用和性能造成负面影响。InnoDB 假定主键可以以数字或字典顺序预测性地递增。如果主键是顺序的,InnoDB 会填满页的 94% ,然后创建新页。然而,如果主键是随机的,利用率可能会低至 50% 。因此,使用包含随机性的 UUID 可能导致索引占用过多页资源。

MySQL中使用UUID主键的最佳实践

如果确实需要在表中使用 UUID 作为记录的唯一标识符,可以遵循以下最佳实践来尽量减少负面影响:

使用二进制数据类型

虽然 UUID 经常以 36 字符长的字符串 表示,但它们也可以表示为原生的二进制形式。如果将 UUID 转换为二进制格式,存储在 BINARY(16) 列中,每个值的存储需求将减少到 16 字节 。虽然仍明显大于 32 位整数,但已经远好于存储为 CHAR(36)

CREATE TABLE uuids(
  UUIDAsChar CHAR(36) NOT NULL,
  UUIDAsBinary BINARY(16) NOT NULL
);
​
INSERT INTO uuids SET
  UUIDAsChar = \'d211ca18-d389-11ee-a506-0242ac120002\',
  UUIDAsBinary = UUID_TO_BIN(\'d211ca18-d389-11ee-a506-0242ac120002\');
​
SELECT * FROM uuids;
-- +--------------------------------------+------------------------------------+
-- | UUIDAsChar                           | UUIDAsBinary                       |
-- +--------------------------------------+------------------------------------+
-- | d211ca18-d389-11ee-a506-0242ac120002 | 0xD211CA18D38911EEA5060242AC120002 |
-- +--------------------------------------+------------------------------------+

使用有序 UUID 变体

使用支持 排序 的 UUID 版本可以减少使用 UUID 带来的性能和存储影响,因为生成的值更接近于顺序状态,从而避免部分页拆分问题。

即使在多个系统间生成,基于时间的 UUID(例如版本 6 或版本 7)也能保证唯一性,同时尽量保持顺序。有一个例外是 UUID 版本 1,它将时间戳中最不重要部分放置优先。

使用MySQL的内置UUID函数

MySQL 支持直接在 SQL 中生成 UUID,但仅支持版本 1 的值。尽管单独使用它们不是最佳做法,但 MySQL 提供了一个名为 uuid_to_bin 的辅助函数,不仅能将字符串值转换为二进制,还支持使用 “swap flag”(交换标志),使生成的二进制值更具顺序性。

SET @uuidvar = \'d211ca18-d389-11ee-a506-0242ac120002\';
​
-- 不使用 swap flag
SELECT HEX(UUID_TO_BIN(@uuidvar)) AS UUIDAsHex;
-- +----------------------------------+
-- | UUIDAsHex                        |
-- +----------------------------------+
-- | D211CA18D38911EEA5060242AC120002 |
-- +----------------------------------+
​
-- 使用 swap flag
SELECT HEX(UUID_TO_BIN(@uuidvar,1)) AS UUIDAsHex;
-- +----------------------------------+
-- | UUIDAsHex                        |
-- +----------------------------------+
-- | 11EED389D211CA18A5060242AC120002 |
-- +----------------------------------+

使用替代的 ID 类型

UUID 不是分布式架构中唯一提供唯一性保障的标识符格式。自 UUID 于 1987 年首次创建以来,已经提出了许多不同的格式,例如 Snowflake ID、ULIDNanoID (PlanetScale 使用 NanoID)。

# Snowflake ID
7167350074945572864
​
# ULID
01HQF2QXSW5EFKRC2YYCEXZK0N
​
# NanoID
kw2c0khavhql

总结

在 MySQL 中使用 UUID 主键在分布式系统中可以几乎确保唯一性;然而它也带来了显著的权衡。幸运的是,通过多个版本的 UUID 以及其替代选项,可以找到更好应对这些权衡的方法,可根据具体的业务场景来选择更好的实践方案。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 在MySQL中,是否可以使用UUID作为主键? https://www.zuozi.net/35861.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务