写点什么

MySQL 自增 ID 用完了怎么办?4 种解决方案!

作者:Java你猿哥
  • 2023-04-11
    湖南
  • 本文字数:2176 字

    阅读完需:约 7 分钟

MySQL 自增 ID 的原理

MySQL 的自增 ID 是通过自动增量机制生成的。当创建一张新表并定义了一个自增列时,MySQL 会在表中创建一个叫做 AUTO_INCREMENT 的计数器。每当插入一行新数据时,MySQL 会自动将这个计数器的值加一,并将这个新的值插入到自增列中。这样,每一行数据都会拥有一个唯一的自增 ID。

默认情况下,自增 ID 的起始值是 1,并且每次自增 1。这个起始值可以通过 ALTER TABLE 语句来更改。如果您需要在表中使用自定义的起始值,可以使用以下命令:

ALTER TABLE my_table AUTO_INCREMENT = 1000;
复制代码

如果您需要查看自增 ID 的当前值,可以使用以下命令:

SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'my_database' AND TABLE_NAME = 'my_table';
复制代码

当自增 ID 用完时会发生什么?

分为两种情况来讨论,一种是指定了主键,一种是未指定主键,我们先来看第一种情况

当您插入大量数据到表中时,自增 ID 计数器的值可能会增加到非常大的数值,直到它达到 INT 或 BIGINT 数据类型的最大值。如果您继续插入数据,MySQL 会尝试将自增 ID 的值增加 1,但由于数据类型的限制,它将无法递增并会抛出一个错误。

例如,如果您的表使用 INT 数据类型,最大值为 2147483647,如果自增 ID 的值已经达到这个最大值,那么 MySQL 将无法再生成新的自增 ID,这时您将无法插入新的记录。

第二种情况,未指定主键,那么 InnoDB 会给你创建一个不可见的,长度为 6 个字节的 row_id。InnoDB 维护了一个全局的 dict_sys.row_id 值,所有无主键的 InnoDB 表,每插入一行数据,都将当前的 dict_sys.row_id 值作为要插入数据的 row_id,然后把 dict_sys.row_id 的值加 1。

实际上,在代码实现时 row_id 是一个长度为 8 字节的无符号长整型 (bigint unsigned)。但是,InnoDB 在设计时,给 row_id 留的只是 6 个字节的长度,这样写到数据表中时只放了最后 6 个字节,所以 row_id 能写到数据表中的值,就有两个特征:

  • row_id 写入表中的值范围,是从 0 到 248-1;

  • 当 dict_sys.row_id=2^48 时,如果再有插入数据的行为要来申请 row_id,拿到以后再取最后 6 个字节的话就是 0。

虽然,2^48 这个数字已经很大了,但是大家要知道 一个系统是可以跑很久的,那么还是可能达到上限的,这时候再申请就会覆盖原来的记录了。因此,尽量不要选择这种方式!

解决办法

解决方案 1:使用 BIGINT 数据类型

一种解决方法是使用 BIGINT 数据类型。BIGINT 数据类型的最大值是 9223372036854775807,这比 INT 数据类型大得多。如果您使用 BIGINT 数据类型来存储自增 ID,那么您的表可以插入更多的数据,而不会出现自增 ID 用完的情况。

但是,使用 BIGINT 数据类型也有一些缺点。首先,它需要更多的存储空间,因为 BIGINT 数据类型需要 8 个字节,而 INT 数据类型只需要 4 个字节。其次,使用 BIGINT 数据类型可能会影响查询的性能,因为 MySQL 需要处理更大的数据块。

解决方案 2:重新设置自增 ID 的起始值

另一种解决方法是重新设置自增 ID 的起始值。通过使用 ALTER TABLE 语句,您可以将自增 ID 的起始值重置为一个更大的数字。例如,如果您的自增 ID 已经达到了 2147483647,您可以使用以下命令将自增 ID 的起始值重置为 3000000000:

ALTER TABLE my_table AUTO_INCREMENT = 3000000000;
复制代码

这样,您就可以再次向表中插入新的数据记录。

但是,这种方法有一些限制。首先,您需要确保自增 ID 的起始值足够大,以便在表中插入足够的记录。如果您的表只能容纳 2147483647 条记录,即使您将自增 ID 的起始值重置为 3000000000,您仍然无法插入更多的记录。

其次,重新设置自增 ID 的起始值可能会导致一些问题。例如,如果您在插入新记录之前删除了一些记录,则新记录可能会拥有一个已经被使用过的自增 ID。这可能会导致唯一性约束的冲突。

解决方案 3:使用分布式 ID 生成器

另一种解决方案是使用分布式 ID 生成器。分布式 ID 生成器可以生成全局唯一的 ID,而不受单个数据库或表的限制。例如,Twitter 的 Snowflake 算法就是一种分布式 ID 生成器。

Snowflake 算法生成的 ID 是一个 64 位的整数,其中包括一个 41 位的时间戳、10 位的工作机器 ID 和 12 位的序列号。Snowflake 算法可以保证在不同的机器上生成的 ID 是唯一的,同时保证生成的 ID 是递增的,这使得它非常适合作为全局唯一的 ID。

使用分布式 ID 生成器的好处是,您可以在任何时候生成新的 ID,而不必担心自增 ID 用完的问题。但是,使用分布式 ID 生成器也有一些缺点。

首先,生成全局唯一的 ID 需要一些计算和存储资源。这意味着您的应用程序需要在生成 ID 时进行额外的计算,并在存储 ID 时使用更多的存储空间。

其次,分布式 ID 生成器也有可能导致一些性能问题。由于 ID 生成器是分布式的,不同的节点可能需要协调以确保生成的 ID 是唯一的。这可能会导致一些延迟和额外的网络开销。

解决方案 4:使用 UUID

最后一个解决方案是使用 UUID(通用唯一标识符)。UUID 是一个 128 位的标识符,可以保证全球唯一。您可以使用 UUID 作为主键来代替自增 ID。

使用 UUID 的好处是,您不必担心 ID 用完的问题,因为 UUID 的数量非常庞大,远远超过自增 ID 的数量。而且,UUID 是全球唯一的,因此您可以将其用于分布式环境中的多个节点。

但是,使用 UUID 也有一些缺点。首先,UUID 的长度远远超过自增 ID,这意味着在存储和索引 UUID 时需要更多的存储和计算资源。

其次,使用 UUID 作为主键可能会导致性能问题。由于 UUID 是随机生成的,而不是递增的,这可能会导致索引效率低下。如果您的表中有大量的记录,使用 UUID 作为主键可能会导致查询性能下降。

用户头像

Java你猿哥

关注

一只在编程路上渐行渐远的程序猿 2023-03-09 加入

关注我,了解更多Java、架构、Spring等知识

评论

发布
暂无评论
MySQL自增ID用完了怎么办?4种解决方案!_Java_Java你猿哥_InfoQ写作社区