Code前端首页关于Code前端联系我们

当InnoDB和row_id自增键用完后会发生什么?

terry 2年前 (2023-09-26) 阅读数 47 #数据库

如果自动加速键用完会怎样?

创建表后,在索引列上添加属性AUTO_INCRMENT(注意:必须是索引列),如下:

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)
复制代码

然后,c1 列的值不再显式插入到句子INSERT 中。它的默认值会从当前插入的最大值开始,稍后会自动增加,例如:

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_INCRMENT作为表的主键。如果这段时间自增列值达到了主键对应的数据类型可以存储的最大值,就会报错(因为主键值重复了),大家注意了!

row_id用完后会发生什么?

当我们使用存储引擎InnoDB创建表时,如果我们自己没有显式创建主键,存储引擎会默认查找具有NOT NULL属性的唯一键 二级索引列作为主键。如果我们在建表语句中没有写唯一一个属性为NOT NULL的二级索引列,那么不幸的是存储机制会默认给我们添加一个所谓的二级索引列。是主键列 row_id

该列row_id默认为6字节。值得注意的是,设计InnoDB的大叔并没有显式地为每个用户创建主键表row_id列维护一个单独的计数器,而是所有表共享一个全局计数器。例如,如果我们没有显式地为表 t1t2 创建主键,则存储引擎会为所有表创建一个列 row_id。如果我们向表添加一列,将一条记录插入到t1,那么全局计数器的值将被指定为表的row_id列的值,然后全局计数器加1;然后我们将表t2加1我们在中插入一条记录,然后将全局计数器的值分配为表的row_id列的值,然后增加全局计数器1.

很多同学表示怀疑。如果这个全局计数器的值超过了6个字节可以表示的最大值,会发生什么情况呢?全局计数器会从0重置并重新开始吗?

哈哈?,这不是真的。虽然row_id由6个字节组成,但是设计InnoDB的大叔使用了8个字节来存储全局计数器值。这8个字节被写入两次。输入row_id,先将右边的四个字节写入row_id的右边四个字节,然后将左边的四个字节写入row_id左边的2个字节,像这样: InnoDB自增键和row_id用完了会发生什么?

下面的函数用于向全局计数器左侧写入 4 个字节,然后向 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);
复制代码

这是一个断言函数。这行代码的含义是,全局计数器n左侧的4字节值不能超过2个字节可存储。最大值,这样的程序就会退出并转储~但是6个字节就足够了。如果你想让MySQL中止,你能计算出要插入多少条记录吗?

作者:小孩子4919
链接:https://juejin.im/post/5e1525a65188253a833b23f9
来源:掘金
版权归作者所有。商业转载请联系作者获得许可。非商业转载请注明来源。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门