考虑到可扩展性和友好 URL 的 GUID 替代方案
Posted
技术标签:
【中文标题】考虑到可扩展性和友好 URL 的 GUID 替代方案【英文标题】:Alternative to GUID with Scalablity in mind and Friendly URL 【发布时间】:2012-10-07 02:46:00 【问题描述】:我决定使用 GUID 作为我的许多项目数据库表的主键。我认为这是一个很好的做法,尤其是考虑到可扩展性、备份和恢复。问题是我不想使用常规 GUID 并搜索替代方法。我实际上很想知道我使用什么 Pinterest 作为主键。当您查看 URL 时,您会看到如下内容:
http://pinterest.com/pin/275001120966638272/
我更喜欢数字表示,即使它是以字符串形式存储的。有什么方法可以实现吗?
此外,youtube 还使用了另一种我无法弄清楚的散列技术:
http://www.youtube.com/watch?v=kOXFLI6fd5A
这让我想起了像方案一样缩短网址。
我更喜欢最短的,但我知道它不能保证是唯一的。我首先想到做这样的事情:
DateTime dt1970 = new DateTime(1970, 1, 1);
DateTime current = DateTime.Now;
TimeSpan span = current - dt1970;
结果示例:
1350433430523.66
打印自 1970 年以来的总毫秒数,但是如果我每秒有数十万次写入会发生什么。
我主要更喜欢非 BIGINT Auto-Increment 解决方案,因为使用 3rd 方工具扩展数据库和备份/恢复功能问题更少,因为我可以在服务器之间传输数据等,如果我愿意的话。
另一种复杂的方法是针对我的应用程序定制解决方案。在数据库中,主键还将包含用户名(唯一且用户无法更改),因此我可以将名称的数值与毫秒数结合起来,这将给我一个唯一的数字字符串。因为用户插入数据的频率不高,所以保证数字ID是唯一的。我也可以删除最后 5 个数字,仍然得到一个唯一的 ID,因为我假设用户插入数据的速度不会超过每秒 1 个,但我可能不会那样做(你怎么看这个想法?)
所以我请求你的帮助。我的数据假设增长非常大,每年 2TB,每秒有数万行新行。我希望 URL 看起来尽可能“友好”,并且不希望使用“常规”GUID。
我正在使用 ASP.NET 4.5 和 mysql 开发我的应用程序
谢谢。
【问题讨论】:
每秒有数万个新项目,我不明白您如何才能保持 URL 的“友好”。 pinterest 如何使用这些数字,我该如何实现? 您如何确定 pinterest(和其他人)不使用 bigint 方法?老相关问题:***.com/questions/2827627/… 【参考方案1】:唯一键的第一个、最简单且实用的方案 是写顺序的递增编号序列, 这表示一个数据库中的记录编号,在本地范围内提供唯一编号:这是 -- 经常满足 -- 应用程序级别要求。
接下来,通常使用基于时间和计数器串联的数值方法来确保同一货车中的并发事务在写入之前将具有唯一的 ID。
当系统变得高度线程化和分布式时,例如在高度并发的情况下,是否需要放松一些约束,以免它们成为扩展的代价。
作为主键的通用唯一标识符
是的,这是一个很好的做法。
关键参考系统可以独立于底层数据库系统。 当诱发场景发生时,这为数据库提供了更高级别的完整性:备份、恢复、扩展、迁移,并可能证明某些真实性。本文Generating Globally Unique Identifiers for Use with MongoDB 作者 Alexander Marquardt(MongoDB 的高级咨询工程师)详细介绍了这个问题,并提供了有关数据库和信息学的一些见解。
UUID 是 128 位长度。他们引入了一定量的熵 足够高以确保标签的实际唯一性。 它们可以用 32 个十六进制字符串表示。 足以写几千亿 十进制数。
以下是在考虑总体原理和分析时可能会出现的几个问题:
-
应该是数据库的主键
和唯一资源位置作为两个不同的实体保存?
此编号是否会破坏系统中的顺序性?
是否提供机器主机号
(h)
,
后跟用户编号(u)
和时间(t)
以及写入索引(i)
保证 PK huti
保持独一无二?
现在考虑数据库系统:
主键应保留为数字(可以是十六进制) 数据库系统依赖于它,这意味着性能方面的考虑。 它们的大小应该是固定的, 系统必须快速响应以判断它是否可能处理 PK。哈希值
Youtube 的哈希技术是hashids。
这是一个不错的选择: 哈希是短裤,长度可以控制, 字母表可以定制, 它是可逆的(并且像对主键的简短引用一样有趣), 它可以使用盐。 它旨在散列正数。
然而它是一个散列,因此存在发生冲突的概率。可以检测到它们:在存储它们之前违反了唯一约束,在这种情况下,应该再次运行。
考虑对此答案的评论,以确定从缩短的 sha1+b64 配方中可以获得多少熵。 To anticipate on the colliding scenario, 要求估计数据库的未来维度,即潜在的记录数。推荐阅读:Z.Bloom, How Long Does An ID Need To Be ?
自纪元以来的毫秒数
引用自上一篇文章,该文章以一种不错的合成风格为手头的问题提供了大部分答案
自 1970 年以来,您可能不需要每次都进行编码 然而。如果您只想保持最近的记录接近 彼此,你只需要足够的值来确保你没有 具有相同前缀的值比您的数据库一次可以缓存的值多
【讨论】:
【参考方案2】:碰撞表
对于类似 GUID 的 YouTube,您可以看到 this 的答案。他们基本上保留了他们生成的所有随机视频 ID 的数据库表。当他们请求一个新的时,他们检查表是否有任何冲突。如果他们发现一个碰撞,他们会尝试生成一个新的。
长主键
您可以使用long
(例如275001120966638272
)作为主键,但是如果您有多个生成唯一标识符的服务器,您将不得不以某种方式对它们进行分区或引入全局锁,因此每个服务器都不会生成相同的唯一标识符。
Twitter 雪花 ID
long
ID 的分区问题的一个解决方案是使用雪花 ID。这就是 Twitter uses 生成它的 ID。所有生成的 ID 都由以下部分组成:
保留一个额外的位以供将来使用。由于 ID 使用时间戳作为第一个组件,因此它们可以按时间排序(这对查询性能非常重要)。
Base64 编码的 GUID
您可以使用ShortGuid 将GUID
编码为base64 字符串。缺点是输出有点难看(例如00amyWGct0y_ze4lIsj2Mw
),并且它区分大小写,如果您使用小写它们可能对 URL 不利。
Base32 编码的 GUID
还有GUID
的base32 编码,你可以看到this 的答案。它们比上面的 ShortGuid 稍长(例如lt7fz44kdqlu5pt7wnyzmu4ov4
),但优点是它们都可以小写。
多重因素
我一直在考虑的一种选择是引入多个因素,例如如果 Pintrest 使用用户名和 ID 来增加唯一性:
https://pinterest.com/some-user/1
这里的 ID 1
对用户 some-user
是唯一的,并且可能是他们发布的帖子数,即他们的下一个帖子将是 2
。您也可以将 YouTube 的方法与他们的视频 ID 一起使用,但特定于用户,这可能会导致一些可笑的短 URL。
【讨论】:
【参考方案3】:您可以通过将 GUID 中的所有字母转换为数字来将 GUID 转换为仅数字。这是一个看起来像的例子。它有点长,但如果这不是问题,这可能是生成密钥的一种方法。
1004234499987310234371029731000544986101469898102
这是我用来生成上述字符串的代码。但是我可能会建议您使用长主键,尽管它可能会有点痛苦,但它可能是比下面的函数更安全的方法。
string generateKey()
Guid guid = Guid.NewGuid();
string newKey = "";
foreach(char c in guid.ToString().Replace("-", "").ToCharArray())
if(char.IsLetter(c))
newKey += (int)c;
else
newKey += c;
return newKey;
编辑:
我做了一些测试,只取前 20 个数字,在 5000000 个生成的密钥中,4999978 是唯一的。但是当使用 25 个第一个数字时,它是 5000000 中的 5000000。如果使用这种方法,我建议您进行更多测试。
【讨论】:
以上是关于考虑到可扩展性和友好 URL 的 GUID 替代方案的主要内容,如果未能解决你的问题,请参考以下文章