你对于在Web应用程序中使用UUID作为数据库行标识符有什么看法?

83

出于简单和(假定的)速度考虑,我一直喜欢在数据库中使用长整型作为主键。但是,当使用类似REST或Rails的URL方案来表示对象实例时,我会得到以下这样的URL:

http://example.com/user/783

假设这个网络应用程序足够安全,可以防止未经授权的人输入其他数字查看其他用户,则假设还有ID为782、781、...、2和1的用户。一个简单的顺序分配的替代键也会“泄漏”实例(在此之前)的总数(例如,我是stackoverflow上的第726个用户),这可能是特权信息。
使用UUID/GUID是否更好?那么我可以设置像这样的URL:
http://example.com/user/035a46e0-6550-11dd-ad8b-0800200c9a66

虽然不是特别简洁,但显示的用户暗示信息较少。当然,这似乎是“安全通过混淆”的表现,这并不能取代适当的安全措施,但它至少看起来更加安全一些。

那么,实现可寻址 Web 对象实例的 UUID 的成本和复杂性是否值得这种好处呢?我想我仍然希望使用整数列作为数据库 PK,以加快联接速度。

还有一个问题,就是 UUID 在数据库中的表示方式。我知道 MySQL 将其存储为 36 个字符的字符串。Postgres 似乎有一个更有效的内部表示 (128 位?),但我自己没有试过。有人有这方面的经验吗?


更新:对于那些询问仅在URL中使用用户名(例如http://example.com/user/yukondude)的人,对于具有唯一名称的对象实例来说,这很好用,但是对于只能通过编号标识的无数Web应用程序对象呢? 订单、交易、发票、重复的图像名称、stackoverflow问题等。

16个回答

34

关于您的问题中的Web部分,我无法发表意见。但对于n-tier应用程序来说,uuid非常适用。PK生成可以被分散化:每个客户端生成自己的PK而不会有冲突风险。速度差异通常很小。

确保您的数据库支持高效的存储数据类型(16字节,128位)。至少可以将uuid字符串编码为base64并使用char(22)。

我在Firebird中广泛使用它们,并推荐使用。


19
如果你没有原生的UUID数据类型,可以去掉破折号并使用32字节的字节码。这样做可能比在需要UUID时进行base64编解码更快。 - CMircea

27

就我所知,我曾经看到一个运行时间较长的存储过程(超过9秒)仅通过从GUID主键切换至整数主键,其运行时间降至几百毫秒。这并不是说在界面上展示GUID是个坏主意,但正如其他人指出的那样,在连接和索引GUID方面,与整数相比,速度肯定不会快。


1
如果您能提供更多关于您看到这个问题的具体信息,将会很有帮助。数据库/表的大小?数据库后端?访问模式(查询是什么样子)...等等? - Garen
17
这怎么算是一个答案呢? - davidahines
20
这是一个支持数学理论的趣闻轶事证据,即连接和索引整数比长字符串更快。 - Adam Tuttle
UUID在数据库中不是字符串。它们应该使用UUID类型存储,这是一种二进制格式。使用字符串列是不正确的,并会带来您提到的性能缺陷。也许当答案编写时(2008年),UUID数据库类型还不存在。 - Vincent

23

如果在SQL Server中使用唯一标识符(GUID)数据类型并使用NEWID()函数创建值,则会因为页面拆分而出现严重的碎片化。原因是使用NEWID()生成的值不是连续的。 SQL 2005添加了NEWSEQUANTIAL()函数来解决这个问题。

仍然可以使用GUID和int的一种方法是在表格中有一个GUID和int,使得GUID映射到int。GUID在外部使用,但int在数据库内部使用。

例如

457180FB-C2EA-48DF-8BEF-458573DA1C10    1
9A70FF3C-B7DA-4593-93AE-4A8945943C8A    2

1和2将用于连接操作,而guids则用于Web应用程序。这个表会很窄,查询速度应该很快。


10
为什么要将主键与URI结合?
为什么不使用可读(或根据需要不可猜测)的URI键和基于整数的主索引,这样你就可以同时获得最佳效果。很多博客软件都是这样做的,入口点的暴露id由“slug”标识,数字id则隐藏在系统内部。
额外的好处是现在你有了一个非常好的URL结构,这对SEO很有好处。显然,这对于交易来说并不是一件好事,但对于像stackoverflow这样的网站来说很重要(请参见上面的URL...)。 获取唯一性并不那么困难。如果你真的很担心,可以在某个表中存储slug的哈希值,并在插入之前进行查找。
编辑:stackoverflow并没有完全使用我描述的系统,请参见Guy下面的评论。

8
Stack Overflow的索引是基于ID而不是slug。尝试更改页面顶部的slug并按Enter键。它会将您重定向到基于ID(5949)而非slug的规范URL,并忽略slug。在服务器上,它会将slug与存储/生成的slug进行比较。如果不同,则返回301。但是它会通过查找ID(5949)来找到匹配的内容。 - Guy

4

不要使用这样的URL:

http://example.com/user/783

为什么不使用:

http://example.com/user/yukondude

哪种技术对人类更友好,且不会泄露任何信息?

4
你可以使用一个与行号相关但不是连续的整数。例如,你可以采用固定方案重新排列顺序ID的32位(例如,第1位变成第6位,第2位变成第15位等)。这将是双向加密,你可以确保两个不同的ID始终具有不同的加密。如果有足够的时间生成足够的ID并获得方案,则很容易解码。但是,如果我正确理解你的问题,你只是不想轻易地泄露信息。

我认为问题的意图并不是要找到一种安全使用UUID的方法。据我所理解,主题是该决策的实际影响。 而你的方案并没有增加任何安全性,只是浪费了CPU周期! - Patrick Cornelissen

4

我们将GUID用作所有表的主键,因为它还可以用作MS SQL Server复制的RowGUID。当客户突然在世界另一个地方开设办事处时,这样做非常容易...


3
我认为GUID并没有太多的好处。用户讨厌过长、难以理解的URL。
创建一个较短的ID,将其映射到URL,或者强制执行唯一的用户名约定(例如:http://example.com/user/brianly)。当涉及到Web应用程序时,37Signals的人可能会嘲笑你担心这样的事情。
顺便提一下,您可以强制数据库从基值开始创建整数ID。

这是不适用的,您不需要在URL中显示uuid。 - davidahines
4
问题提问者在问题中提到了在URL中使用它。 - Brian Lyttle

3

这也取决于您的应用程序关心什么。对于n层应用程序,GUIDs / UUIDs更简单易于实现,并且更容易在不同的数据库之间移植。为了生成整数键,一些数据库本地支持序列对象,而一些则需要自定义构建序列表。

整数键可能(我没有数字)提供查询和索引性能以及空间使用方面的优势。使用数字键进行直接数据库查询也更加容易,减少复制/粘贴,因为它们更容易记住。


2

我在真实的网络应用程序中都尝试过。

我的观点是,最好使用整数并拥有短而易懂的URL。

作为开发者,看到连续的整数并知道某些关于总记录数的信息正在泄露出去,感觉有点可怕,但老实说-大多数人可能并不在意,而且这些信息从来没有真正关键到我的业务。

对普通用户而言,使用长而丑陋的UUID URLs似乎更加让人反感。


谢谢您的意见。我研究了使用UUID作为主键及其所有可能的缺点,直到我意识到在我的情况下唯一的优点(隐藏业务信息)不值得这样做。 - Dr. Jan-Philip Gehrcke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接