温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

InnoDB中的row_id与自增键用完怎么办

发布时间:2020-06-10 15:31:10 来源:亿速云 阅读:416 作者:元一 栏目:编程语言

在数据库表设计中会纠结于”自然键”和”代理键”的选择,自然键在实现数据“软删除”时实现比较复杂,部分自然键因为键值过长或多列组合导致不适合作为表主键,而比较常见两种代理键为自增列(auto incremnet)和全局唯一标识列(GUID)。

使用自增列作为主键的优缺点:

1、 主键键值长度短,INT列需要4个字节,BIGINT列需要8个字节;

2、 自增主键顺序递增,在INSERT操作时”顺序”写入表;

3、 由于数据集中插入到表尾部,在高并发情况下容易造成”数据页热点”,影响插入效率;

4、 自增主键只能保证在表内数据唯一,对于分库分表场景,可能因错误操作产生相同的“唯一值”。

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

我们在建表的时候为某个索引列( 注意:必须是索引列 )添加 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 列都单独维护一个计数器,而是所有的表都共享一个全局的计数器。比方说我们没有对表 t1 和 t2 显式创建主键,存储引擎为它们都创建了一个 row_id 列,如果我们向表 t1 中插入了一条记录,那么就从全局计数器里分配一个值做该表 row_id 列的值,然后将全局计数器加1;接着我们再向表 t2 中插入一条记录,那么就再从全局计数器里分配一个值做该表 row_id 列的值,然后将全局计数器加1。

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

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

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

UNIV_INLINEvoidmach_write_to_2(/*============*/byte*b,/*!< in: pointer to two bytes where to store 也就是row_id值前2个字节所在的内存地址*/ulintn)/*!< 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挂掉需要插入多少条记录呢?

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI