当我用尽 bigint 生成的密钥时会发生啥?如何处理?
Posted
技术标签:
【中文标题】当我用尽 bigint 生成的密钥时会发生啥?如何处理?【英文标题】:What happens when I exhaust a bigint generated key? How to handle it?当我用尽 bigint 生成的密钥时会发生什么?如何处理? 【发布时间】:2012-10-19 10:26:37 【问题描述】:我无法为自己想象一个好的答案,所以我想在这里问它。在我的脑海中,我总是想知道如果我的 MySQL 表中的AUTO INCREMENT PRIMARY ID
列用完会发生什么?
例如,我有一个有两列的表。 ID (auto increment, primary, BIGINT unsigned
) 和 DESC (VARCHAR 255
)。我肯定知道BIGINT
很多,但它可以达到它的极限。我该如何处理 in case ID 达到其限制的情况?我需要另一台服务器吗?如果那么我该如何同步它?这是正确的方法吗?有什么见解的朋友。
【问题讨论】:
您想知道这个只是出于好奇吗?如果是这样,编写一些代码来测试自己应该不难。如果您担心大量记录并且想避免这个问题(如果这是一个问题),那么根本不要使用数字并转而使用 uniqueidentifiers。 很好奇。我习惯于建立一些网站,但并不完全产生大量流量,所以我看不到自己达到了极限。你说得对,我担心这么多的记录。看到uniqueidentifiers,你是不是和UUID有关? 这是一个真实的问题吗?在达到 9 个 Quintillion 记录之前,您不会遇到各种实际问题吗? 是的,我经常使用它们来避免类似的问题 【参考方案1】:它不会用完。
最大的 bigint 是 9223372036854775807 。以 1000 次插入/秒的速度,这相当于 106751991167 天。差不多 3 亿年,如果我的数学正确的话。
即使您将其划分出来,使用偏移量,例如 100 个服务器每个都有一个专用的值子范围 (x*100+0
...x*100+99
),您也不会用完。 10,000 台机器每秒执行 100,000 次插入可能会在大约三个世纪内让您到达那里。当然,这比数百年来稳定的纽约证券交易所每秒的交易量还要多……
如果您超过了生成键的数据类型大小限制,新插入将失败。在带有 bigserial
的 PostgreSQL 中(因为你已经标记了这个 PostgreSQL),你会看到:
CREATE TABLE bigserialtest ( id bigserial primary key, dummy text );
SELECT setval('bigserialtest_id_seq', 9223372036854775807);
INSERT INTO bigserialtest ( dummy ) VALUES ('spam');
ERROR: nextval: reached maximum value of sequence "bigserialtest_id_seq" (9223372036854775807)
对于普通的serial
,您会得到不同的错误,因为sequence
始终是 64 位的,因此您必须将密钥类型更改为 bigint
或获取像这样的错误:
regress=# SELECT setval('serialtest_id_seq', 2147483647);
regress=# INSERT INTO serialtest (dummy) VALUES ('ham');
ERROR: integer out of range
如果您确实相信您的网站有可能达到应用程序中 bigint 的限制,您可以使用复合键 - 例如 (shard_id, subkey) - 或 uuid 键。
试图在新应用程序中处理这个问题是过早的优化。说真的,从一个新的应用程序到那种增长,你会使用相同的模式吗?还是数据库引擎?甚至是代码库?
您不妨担心 GUID 键控系统中的 GUID 冲突。毕竟,生日悖论意味着GUID collisions are more likely than you think - 难以置信,非常不可能。
此外,正如 Barry Brown 在 cmets 中指出的那样,您永远不会存储那么多数据。这只是对交易率极高的高流失率表的关注。在这些表中,应用程序只需要能够应对被重置为零的键、重新编号的条目或其他应对策略。不过,老实说,即使是高流量的消息队列表也不会达到顶峰。
见:
this IBM info on serial exhaustion A recent blog post on this topic说真的,即使您构建了下一个 Gootwitfacegram,这也不会成为问题,直到您第三次重写应用程序的使用日期...
【讨论】:
问题不在于它是否用完,问题在于如何设计表以避免对增量主键的任意限制。 @BurhanKhalid 我称之为过早优化。你真的认为你会使用相同的模式从一个新的应用程序到那种增长吗?甚至是相同的代码库? 那些 9223372036854775807 bigint 将需要 67,108,863 TB 来存储。有那么多磁盘空间吗? @BarryBrown 当桌面上的流失率非常高时,这是一个合理的担忧。我当然看到正常SERIAL
列的报告(其中存储数据类型是integer
而不是bigint
,即使序列仍然是bigint
)命中MAXINT。
@BurhanKhalid 绝对是一切都在把罐子踢下去,问题是你需要踢多远?文件系统有大小限制(尽管其中一些限制是艾字节),操作系统对它们可以挂载的文件系统数量、可以寻址的磁盘等有限制。时钟对其可以表示的时间有上限。理智的方法是将其踢得足够远,以使其他限制首先生效,然后停止关心。试图想出完美的解决方案让您的应用在 10,000 年内保持不变运行是没有意义的。【参考方案2】:
大整数是 2^63 或大约 10^19。 几年前,数据库基准测试曾经风靡一时,使用标准化的TPC-C Benchmark
如您所见,关系数据库的最快关系得分为 30,000,000(每分钟 3x10^7 个事务)。请记住,配置文件将包含大量读取,同一系统不太可能每分钟写入 30,000,000 行。
假设是这样,您将需要大约 3x10^11 分钟来耗尽 BigInt。在我们理解的时间测量中,这类似于 600 万年
ERROR 1467 (HY000): Failed to read auto-increment value from storage engine
如果您确实用完了,您将收到上述错误消息,然后转到 Guid 获取主键。 2^128,有less digital bits on earth 比那个数字(十亿倍)。
【讨论】:
在方案设计期间至少使用 bigint 而不是 int 是明智的。看起来此操作足以满足大多数实际情况!【参考方案3】:一旦自动增量达到字段大小的限制,INSERT 就会产生错误。
实际上你会得到以下类型的错误:
ERROR 1467 (HY000): Failed to read auto-increment value from storage engine
欲了解更多信息,请访问:
http://dev.mysql.com/doc/refman/5.1/en/example-auto-increment.html
【讨论】:
【参考方案4】:大型数据集不使用递增数字作为键。不仅因为你有一个隐含的上限,而且当你有多个服务器时也会产生问题;因为你冒着重复主键的风险,因为它们是增量的。
当你在 MySQL 上达到这个限制时,你会得到一个像这样的神秘错误:
Error: Duplicate entry '0' for key 1
最好使用您生成的唯一 ID 或其他序列。 MySQL 不支持序列,但 postgresql 支持。
【讨论】:
是的,通用唯一 ID。以上是关于当我用尽 bigint 生成的密钥时会发生啥?如何处理?的主要内容,如果未能解决你的问题,请参考以下文章