背景
根据前端开发人员的建议,我研究了在我们的新系统中使用 UUID 作为一堆表的主键。从学习随机 UUID 与顺序 UUID 的优缺点,到结合使用非聚集主键和可排序类型的聚集索引,我的研究向我指出了UUIDv6及其实现。
它能够生成如下所示的 UUID(即顺序的):
UUIDv1 UUIDv6 ---------------------------------- -------------- ---------------------- 5714f720-1268-11e7-a24b-96d95aa38c32 1e712685-714f-6720-a23a-c90103f70be6 68f820c0-1268-11e7-a24b-96d95aa38c32 1e712686-8f82-60c0-ac07-7d6641ed230d 7ada38f0-1268-11e7-a24b-96d95aa38c32 1e712687-ada3-68f0-93f8-c1ebf8e6fc8c 8cc06fd0-1268-11e7-a24b-96d95aa38c32 1e712688-cc06-6fd0-a828-671acd892c6a 9ea6a6b0-1268-11e7-a24b-96d95aa38c32 1e712689-ea6a-66b0-910c-dbcdb07df7a4
我认为 SQL Server 会很乐意在集群主键(唯一标识符)列中为我排序。
我几乎不知道 SQL Server 如何对 uniqueidentifier 列进行排序。这是升序排序结果:
UUIDv6 唯一标识符已排序 ---------------------------------- 1e712688-cc06-6fd0- a828-67 1acd892c6a 1e712686-8f82-60c0- ac07-7d 6641ed230d 1e712687-ada3-68f0-93f8- c1 ebf8e6fc8c 1e712685-714f-6720-a23a- c9 0103f70be6 1e712689-ea6a-66b0-910c- db cdb07df7a4
这会导致碎片,就像使用随机 UUID 一样。这篇文章解释了它们实际上是如何排序的。
真正的问题
幸运的是,该系统仍在开发中。接下来我应该选择哪些选项?
- 重新排序字节,使最高/最低有效字节位于 SQL Server 期望的位置
UUIDv6 UUIDv6 重新排序的字节 ---------------------------------- -------------- ---------------------- 1e712685-714f-6720-a23a-c90103f70be6 c90103f7-0be6-a23a-6720- 1e712685 714f 1e712686-8f82-60c0-ac07-7d6641ed230d 7d6641ed-230d-ac07-60c0- 1e712686 8f82 1e712687-ada3-68f0-93f8-c1ebf8e6fc8c c1ebf8e6-fc8c-93f8-68f0- 1e712687 ada3 1e712688-cc06-6fd0-a828-671acd892c6a 671acd89-2c6a-a828-6fd0- 1e712688 cc06 1e712689-ea6a-66b0-910c-dbcdb07df7a4 dbcdb07d-f7a4-910c- 66b0-1e712689 ea6a
- 将 UUIDv6 转换为二进制(16)并改用它
UUIDv6 UUIDv6 二进制(16) ---------------------------------- -------------- ------------------ 1e712685-714f-6720-a23a-c90103f70be6 1e712685 714f6720a23ac90103f70be6 1e712686-8f82-60c0-ac07-7d6641ed230d 1e712686 8f8260c0ac077d6641ed230d 1e712687-ada3-68f0-93f8-c1ebf8e6fc8c 1e712687 ada368f093f8c1ebf8e6fc8c 1e712688-cc06-6fd0-a828-671acd892c6a 1e712688 cc066fd0a828671a cd892c6a 1e712689-ea6a-66b0-910c-dbcdb07df7a4 1e712689 ea6a66b0910cdbcdb07df7a4
有问题option 1
UUID 标准在 ID 中嵌入了一个 4 位版本字段。UUIDv6(仍然是非标准的)也遵循该规则。我将重新排序它们的方式将打破这一点。
有问题option 2
我不确定。除了这个,几乎找不到任何人在谈论它,这与这个想法背道而驰。在使用 binary(16) 类型时我还应该注意其他陷阱吗?
谢谢!
我会考虑另一种选择:在代理键(如
Id int IDENTITY(1,1) NOT NULL
)上进行集群,并使应用程序生成的 UUID 成为非集群主键。这避免了您使用选项 1 和 2 调用的问题,因为您不必担心基表中的排序/碎片问题,或者集群上潜在的深奥问题
binary
。它还会为您节省一些空间(int 小于
uniqueidentifier
andbinary(16)
),因为集群键包含在每个非聚集索引中(以及通过外键引用该表的其他表)。我在 SQL Server 中使用过 BINARY 数据类型,但不记得我是否曾经在 BINARY 列上创建过带有聚集索引的表。话虽如此,我知道唯一需要注意的是文档中的这个注释:
如果您确实使用选项 2,我建议在应用程序代码中在 SQL Server 之外对 BINARY 值进行所有转换和操作。
去做。
选项 1 是 SQL Server 原生使用 NEWSEQUENTIALID() 所做的,正如维基百科所说
在 SQL Server 中,UNIQUEIDENTIFIER 只是一个 128 位二进制类型。它不需要符合 UUID 的结构。
如果它是按顺序生成的*,您可以将其设为聚集索引键。拥有一个更窄的聚集索引键通常不值得拥有一个额外且不必要的索引的成本。
*如果顺序值的排序顺序中的位置偶尔发生变化或由几个不同的应用程序服务器在几个不同的地方生成,这不是什么大问题。