Bootstrap

什么是 UUID,它们为什么有用?

在这里插入图片描述
通用唯一标识符 (UUID) 是一种特定形式的标识符,在大多数实际用途中可以安全地认为是唯一的。两个正确生成的 UUID 相同的可能性几乎可以忽略不计,即使它们是由不同的各方在两个不同的环境中创建的。这就是为什么说 UUID 是普遍唯一的。

在本文中,我们将了解 UUID 的特征、它们的唯一性如何工作以及它们可以简化资源识别的场景。尽管我们将从与数据库记录交互的软件的通用角度来处理 UUID,但它们广泛适用于需要生成去中心化唯一 ID 的任何用例。

UUID 到底是什么?

UUID 只是一个值,您可以放心地将其视为唯一值。碰撞的风险是如此之低,以至于您可以合理地选择完全忽略它。您可能会看到 UUID 使用不同的术语(GUID 或 Globally Unique Identifier,是 Microsoft 的首选语义)来引用,但含义和效果保持不变。

真正的 UUID 是由标准化格式生成和表示的唯一标识符。有效的 UUID 由RFC 4122定义;本规范描述了可用于生成 UUID 的算法,这些算法在实现之间保持唯一性,而无需中央发行机构。

RFC 包括五种不同的算法,每种算法使用不同的机制来产生值。以下是可用“版本”的简要摘要:

  • 版本 1 – 基于时间– 结合时间戳、时钟序列和特定于生成设备的值(通常是其 MAC 地址),以在该时间点为该主机生成唯一的输出。
  • 第 2 版 - DCE 安全性 - 此版本是作为第 1 版的演变而开发的,用于分布式计算环境 ( DCE )。它没有被广泛使用。
  • 版本 3 – 基于名称 (MD5) – MD5
    对“名称空间”和“名称”进行散列处理,以创建名称在名称空间内唯一的值。生成另一个具有相同命名空间和名称的 UUID
    将产生相同的输出,因此此方法提供可重现的结果。
  • 版本 4 - 随机- 大多数现代系统倾向于选择 UUID v4,因为它使用主机的随机或伪随机数源来发布其值。两次生成相同 UUID
    的机会几乎可以忽略不计。
  • 版本 5 - 基于名称 (SHA-1) - 这与版本 3 类似,但它使用更强大的 SHA-1 算法来散列输入命名空间和名称。
    尽管 RFC 将算法称为版本,但这并不意味着您应该始终使用版本 5,因为它似乎是最新的。选择哪一种取决于您的用例;在许多情况下,选择 v4 是因为它的随机性。这使它成为简单的“给我一个新标识符”场景的理想选择。

生成算法发出一个 128 位无符号整数。但是,UUID 更常见的是十六进制字符串,也可以存储为 16 个字符的二进制序列。这是一个 UUID 字符串的示例:

16763be4-6022-406e-a950-fcd5018633ca

该值表示为由短划线字符分隔的五组字母数字字符。破折号不是字符串的强制组成部分;它们的存在取决于 UUID 规范的历史细节。它们还使标识符更容易被人眼感知。

UUID 用例

UUID 的主要用例是去中心化生成唯一标识符。您可以在任何地方生成 UUID 并安全地认为它是唯一的,无论它来自您的后端代码、客户端设备还是您的数据库引擎。

UUID 简化了在断开连接的环境中确定和维护对象身份。从历史上看,大多数应用程序都使用自动递增的整数字段作为主键。当你创建一个新对象时,直到它被插入数据库后你才能知道它的 ID。UUID 让您在应用程序中更早地确定身份。

这是一个演示差异的基本 PHP 演示。我们先来看基于整数的系统:

class BlogPost { 
    public  function __construct ( 
        public readonly ?int $Id , 
        public readonly string $Headline , 
        public readonly ?AuthorCollection $Authors = null )  { } 
}
 
#[POST("/posts")]
 function createBlogPost ( HttpRequest $Request )  : void { 
    $headline  =  $Request  ->  getField ( "Headline" ) ; 
    $blogPost  =  new BlogPost ( null ,  $headline ) ; 
}

我们必须初始化 I d 属 性 , 因 为 直 到 它 被 持 久 化 到 数 据 库 之 后 n u l l 我 们 才 能 知 道 它 的 实 际 I D 。 这 并 不 理 想 — — 不 应 该 真 的 可 以 为 空 , 它 允 许 实 例 以 不 完 整 的 状 态 存 在 。 Id属性,因为直到它被持久化到数据库之后null我们才能知道它的实际 ID 。这并不理想——不应该真的可以为空,它允许实例以不完整的状态存在。 IdnullIDIdBlogPost

更改为 UUID 可以解决问题:

class BlogPost { 
    public  function __construct ( 
        public readonly string $Uuid , 
        public readonly string $Headline , 
        public readonly ?AuthorCollection $Authors = null )  { } 
}
 
#[POST("/posts")]
 function createBlogPost ( HttpRequest $Request )  : void { 
    $headline  =  $Request  ->  getField ( "Headline" ) ; 
    $blogPost  =  new BlogPost ( "16763be4-..." ,  $headline ) ; 
}

现在可以在应用程序中生成帖子标识符,而不会冒重复值的风险。这可确保对象实例始终表示有效状态,并且不需要笨拙的可为空 ID 属性。该模型也更容易处理事务逻辑;可以立即插入需要引用其父级的子记录(例如我们的帖子的Author关联),而无需数据库往返来获取分配给父级的 ID。

将来,您的博客应用程序可能会将更多逻辑移至客户端。也许前端获得了对完全离线草稿创建的支持,有效地创建BlogPost了临时保存在用户设备上的实例。现在客户端可以生成帖子的 UUID 并在重新连接网络时将其传输到服务器。如果客户端随后检索到服务器的草稿副本,它可以将其与任何剩余的本地状态相匹配,因为 UUID 已经是已知的。

UUID 还可以帮助您组合来自各种来源的数据。合并使用整数键的数据库表和缓存可能很乏味且容易出错。UUID 不仅在表内而且在整个 Universe 级别都提供唯一性。这使它们成为经常在不同存储系统之间移动的复制结构和数据的更好候选者。

UUID 遇到数据库时的注意事项

UUID 的好处非常引人注目。但是,在实际系统中使用它们时需要注意几个问题。支持整数 ID 的一大因素是它们易于扩展和优化。数据库引擎可以轻松地索引、排序和过滤仅在一个方向上的数字列表。

UUID 不能这样说。首先,UUID 比整数大四倍(36 字节对 4 字节);对于大型数据集,这本身可能是一个重要的考虑因素。这些值的排序和索引也更加棘手,尤其是在最常见的随机 UUID 的情况下。它们的随机性意味着它们没有自然顺序。如果您使用 UUID 作为主键,这将损害索引性能。

这些问题可能会在大量使用外键的规范化数据库中复杂化。现在您可能有许多关系表,每个表都包含对您的 36 字节 UUID 的引用。最终,执行连接和排序所需的额外内存可能会对系统性能产生重大影响。

您可以通过将 UUID 存储为二进制数据来部分缓解这些问题。这意味着一BINARY(16)列而不是VARCHAR(36). 某些数据库(例如 PostgreSQL)包含内置UUID数据类型;其他像 MySQL的函数可以将 UUID 字符串转换为其二进制表示,反之亦然。这种方法更有效,但请记住,您仍将使用额外资源来存储和选择数据。

一个有效的策略是保留整数作为主键,但添加一个额外的 UUID 字段供应用程序参考。当您的代码使用 UUID 获取和插入顶级对象时,关系链接表可以使用 ID 来提高性能。这一切都取决于您的系统、其规模和您的优先事项:当您需要去中心化 ID 生成和直接数据合并时,UUID 是最佳选择,但您需要权衡取舍。

概括

UUID 是您可以安全地用于去中心化身份生成的唯一值。碰撞是可能的,但应该非常罕见,可以将其从考虑中丢弃。如果你在整个世纪内每秒生成 10 亿个 UUID,假设有足够的熵可用,遇到重复的概率约为 50% 。

在插入发生之前,您可以使用 UUID 建立独立于数据库的身份。这简化了应用程序级代码并防止不正确识别的对象存在于您的系统中。与在表级别操作的传统整数键不同,UUID 还通过保证与数据存储、设备或环境无关的唯一性来帮助数据复制。

虽然 UUID 现在在软件开发中无处不在,但它们并不是一个完美的解决方案。新手往往会关注冲突的可能性,但这不应该是您的主要考虑因素,除非您的系统非常敏感以至于必须保证唯一性。
对于大多数开发人员来说,更明显的挑战是生成的 UUID 的存储和检索。天真地使用 a VARCHAR(36)(或去掉连字符并使用VARCHAR(32))可能会随着时间的推移削弱您的应用程序,因为大多数数据库索引优化将无效。研究数据库系统的内置 UUID 处理功能,以确保您从解决方案中获得最佳性能。

;