mysql主键自增长_InnoDB的自增键和row_id用完了会发生什么?

当MySQL的tinyint自增列达到最大值,其不会再增长而是取最大值。若使用InnoDB,row_id用完不会重置,而是触发全局计数器溢出,导致MySQL崩溃。主键列作为唯一标识,达到数据类型最大值时插入新数据会报错。全局计数器用8个字节存储,分两次写入,若超过限制则引发程序崩溃。
摘要由CSDN通过智能技术生成

自增键用完了会发生什么?

我们在建表的时候为某个索引列(注意:必须是索引列)添加AUTO_INCREMENT属性,就像这样:

CREATE TABLE t (
c1 TINYINT AUTO_INCREMENT,
c2 TINYINT,
KEY idx_c1 (c1)
) ENGINE=InnoDB;

t中包含一个索引列c1,该列被添加了AUTO_INCREMENT属性。我们先向该表中插入一条记录:

mysql> INSERT INTO t(c1, c2) VALUES(126, 1);
Query OK, 1 row affected (0.02 sec)

之后我们不再在INSERT语句中显式地插入c1列的值,它的默认值就将是从当前插入的最大的那个值之后自增,比如这样:

mysql> INSERT INTO t(c2) VALUES(1);
Query OK, 1 row affected (0.01 sec)

我们看一下此时表t中的数据:

mysql> SELECT * FROM t;
+-----+------+
| c1 | c2 |
+-----+------+
| 126 | 1 |
| 127 | 1 |
+-----+------+
2 rows in set (0.02 sec)

因为c1列是TINYINT类型的,使用1个字节存储数据,它能存储最大的值就是127,如果当该列的值到达127之后,我们继续向表中插入数据,自增列c1的值将会变成什么呢?

mysql> INSERT INTO t(c2) VALUES(1);
Query OK, 1 row affected (0.01 sec)

插入成功之后我们再看一下表中的数据:

mysql> SELECT * FROM t;
+-----+------+
| c1 | c2 |
+-----+------+
| 126 | 1 |
| 127 | 1 |
| 127 | 1 |
+-----+------+
3 rows in set (0.01 sec)

很显然,自增列c1的值将不再继续增长,而是取的TINYINT所能存储的最大值。

这里需要注意的是,在当前举的例子中,我们只是在自增列c1上边建立了一个普通的二级索引idx_c1,所以键值重复也没啥问题。不过我们一般将AUTO_INCREMENT属性应用在表的主键上,此时如果自增列值达到了主键对应数据类型所能存储的最大值时,就会报错(因为主键值重复),大家一定注意!

row_id用完了会发生什么?

在我们使用InnoDB存储引擎来建表时,如果我们自己没有显式地创建主键时,存储引擎会默认找一个具有NOT NULL属性的唯一二级索引列来充当主键,如果我们在建表语句中也没有写具有NOT NULL属性的唯一二级索引列,那很抱歉,存储引擎默认会为我们添加一个称之为row_id的主键列。

这个row_id列默认是6个字节大小,值得注意的是,设计InnoDB的大叔并不是为每一个用户未显式创建主键的表的row_id列都单独维护一个计数器,而是所有的表都共享一个全局的计数器。比方说我们没有对表t1t2显式创建主键,存储引擎为它们都创建了一个row_id列,如果我们向表t1中插入了一条记录,那么就从全局计数器里分配一个值做该表row_id列的值,然后将全局计数器加1;接着我们再向表t2中插入一条记录,那么就再从全局计数器里分配一个值做该表row_id列的值,然后将全局计数器加1。

有很多同学有疑惑,如果这个全局计数器的值超过了6个字节所能表示的最大值时,会发生什么,全局计数器从0重新开始技术,一切从头再来么?

哈哈?,并不会这样。虽然row_id由6个字节组成,但是设计InnoDB的大叔却是使用8个字节存储全局计数器的值,他们将这8个字节分两次写入row_id,第一次写入右边四个字节到row_id的右边4个字节,接着将左边四个字节再写入row_id的左边的2个字节,就像这样:

a39fe227eb1abf52b06754c331b30982.png

在将全局计数器左边四个字节再写入row_id的左边的2个字节 时采用如下函数:

UNIV_INLINE
void
mach_write_to_2(
/*============*/
byte* b, /*!< in: pointer to two bytes where to store 也就是row_id值前2个字节所在的内存地址*/
ulint n) /*!< in: ulint integer to be stored 也就是全局计数器的左4个字节值*/
{
ut_ad(b);
ut_ad((n | 0xFFFFUL) <= 0xFFFFUL);

b[0] = (byte)(n >> 8);
b[1] = (byte)(n);
}

可以看到代码中有这样一行:

ut_ad((n | 0xFFFFUL) <= 0xFFFFUL);

这是一个断言函数,这行代码的意思就是全局计数器的左边4个字节值n必须不大于2个字节所能存储的最大值,否则的话断言就失败了,然后MySQL就挂掉了,就挂掉了,就挂掉了~

也就是说如果row_id用完了之后MySQL就会挂掉,那种程序直接退出的挂掉~ 不过6个字节已经足够大了,大家可以算算如果想让MySQL挂掉需要插入多少条记录呢?

小青蛙历史文章(历史文章,不容错过):

小青蛙2019年原创文章集锦

如此重要的能力,可惜大部分人没有

容易被忽视的MySQL字符集问题?

MySQL中NULL值引起的小锅

关于事务和锁的一些细节 个人所得税涨了,日子又拮据了一点 补数到底是个什么玩意儿?从根儿上理解一下 MySQL小册创作之心路历程 虚拟内存是个啥 | 一分钟系列 MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!

长按关注小青蛙,都是干货喔

3d4b8ad074b2332754482671d8aed7a2.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值