MySQL PRIMARY KEYs:UUID / GUID vs BIGINT(时间戳+随机)

时间:2011-06-14 04:08:55

标签: mysql sql database performance database-design

tl; dr:如果我不想处理UUID,将{unixtimestamp} {randomdigits}(例如1308022796123456)的行ID分配为BIGINT是一个好主意吗?

只是想知道是否有人对跨多个服务器分配给数据库记录的ID / PRIMARY KEY的任何性能或其他技术考虑因素/限制有所了解。

我的PHP + MySQL应用程序在多台服务器上运行,数据需要能够合并。所以我已经超越了识别行的标准顺序/ auto_increment整数方法。

我对解决方案的研究使我了解了使用UUID / GUID的概念。但是,需要更改我的代码以处理在MySQL中将UUID字符串转换为二进制值似乎有点痛苦/工作。出于存储和性能原因,我不想将UUID存储为VARCHAR。

存储在二进制列中的UUID的另一个可能的烦恼是,当查看PhpMyAdmin中的数据时行ID并不明显 - 尽管我可能错了 - 但直接数字似乎总体上更简单并且是在任何类型的数据库系统中都是通用的,无需转换。

作为一个中间立场,我提出了将我的ID列设为BIGINT,并使用当前的unix时间戳后跟6个随机数字来分配ID的想法。所以我可以说我的随机数是123456,我今天生成的ID会显示为: 1308022796123456

在同一秒内创建的行冲突的可能性为千万分之一。我没有快速做任何类型的大规模行创建。

我用随机生成的UUID读到的一个问题是它们对索引不好,因为值不是连续的(它们遍布整个地方)。 MySQL中的UUID()函数通过从当前时间戳生成UUID的第一部分来解决此问题。因此,我复制了在我的BIGINT开头使用unix时间戳的想法。我的索引会慢吗?

我的BIGINT想法的优点:

  • 为我提供了UUID的多服务器/合并优势
  • 我的应用程序代码需要很少的更改(所有内容都已编程为处理ID的整数)
  • UUID的一半存储空间(8字节对16字节)

缺点:

  • ??? - 如果您能想到,请告诉我。

一些跟进问题与此相关:

  1. 我最后应该使用多于或少于6个随机数字?它会对索引性能产生影响吗?

  2. 其中一种方法是“randomer”吗?:让PHP生成6位数并将它们连接在一起-VS-让PHP生成1 - 999999范围内的数字,然后zerofilling以确保6位数。

  3. 感谢您的任何提示。抱歉,文字墙。

4 个答案:

答案 0 :(得分:14)

我在职业生涯中遇到过这个问题。我们使用时间戳+随机数,并在我们的应用程序扩展时遇到严重问题(更多客户端,更多服务器,更多请求)。当然,我们(愚蠢地)只使用了4位数字,然后更改为6位,但你会惊讶于错误仍然发生的频率。

在足够长的时间内,保证可以获得重复的密钥错误。我们的应用程序是关键任务,因此即使是最小的机会也不可能导致固有的随机行为是不可接受的。我们开始使用UUID来避免这个问题,并仔细管理他们的创建。

使用UUID,您的索引大小将增加,而较大的索引将导致较差的性能(可能不明显,但更差)。然而,MySQL支持本机UUID类型(永远不会使用varchar作为主键!!),并且即使与bigint相比也可以非常有效地处理索引,搜索等。对索引的最大性能影响几乎总是索引的行数,而不是索引项的大小(除非你想在长篇文章上索引或像这样荒谬的东西)。

回答你的问题:如果你不打算大幅扩展你的应用程序/服务,Bigint(附带随机数字)就可以了。如果您的代码可以在没有太多更改的情况下处理更改,并且如果发生重复键错误,您的应用程序将不会爆炸,请使用它。否则,咬一口,寻求更实质的选择。

您可以随时实施更大的更改,例如切换到完全不同的后端(我们现在面临......:P)

答案 1 :(得分:4)

您可以手动更改自动编号起始编号。

ALTER TABLE foo AUTO_INCREMENT = ####

unsigned int最多可存储4,294,967,295,可将其四舍五入为4,290,000,000。

使用服务器序列号的前3位数字,以及行ID的最后7位数字。

这为您提供最多430台服务器(包括000),每台服务器最多可提供1000万个ID。

因此,对于服务器#172,您手动将自动编号更改为1,720,000,000,然后让它按顺序分配ID。

如果您认为可能有更多服务器,但每台服务器的ID较少,则将其调整为每台服务器4位数,将ID调整为6位(即最多1百万个ID)。

您也可以使用二进制数字而不是十进制数字来分割数字(每个服务器可能有10个二进制数字,ID可能为22个。因此,例如,服务器76从2 ^ 22 * 76 = 318,767,104开始,到322,961,407结束)。

就此而言,你甚至不需要明确的分裂。拿4,294,967,295除以你认为你将拥有的最大服务器数量,这就是你的间距。

如果您认为需要更多标识符,可以使用bigint,但这是一个非常庞大的数字。

答案 2 :(得分:0)

如果要使用时间戳方法,请执行以下操作:

为每个服务器添加一个数字,以附加正在执行插入的应用程序(或线程ID)的进程ID(在PHP中为getmypid()),然后添加该进程已存活多长时间/ active(在PHP中是getrusage()),最后在每个脚本调用开始时添加一个从0开始的计数器(即同一个脚本中的每个插入都添加一个)。

此外,您不需要存储完整的unix时间戳 - 大多数这些数字用于表示2011年,而不是1970年。所以如果你不能得到一个数字,说明这个过程有多长,然后至少减去代表今天的固定时间戳 - 这样你就需要少得多的数字。

答案 3 :(得分:0)

使用GUID作为唯一索引,但是计算GUID的64位(BIGINT)哈希,将其存储在单独的NOT UNIQUE列中,然后索引它。要进行检索,请查询两列的匹配项-64位索引应使之有效。

这样做的好处是哈希值:
一种。不必唯一。
b。可能分布均匀。

费用:额外的8字节列及其索引。