MySQL 的自增 id 会用完吗?用完怎么办
MySQL 作为最常用的关系型数据库,无论是在应用还是在面试中都是必须掌握的技能。
一、MySQL 自增主键会用完吗
我们在使用 MySQL 设置的自增主键的时候,一般都是定义初始值和定义步长,我们知道自然数是没有上限的,但是 MySQL 的自增主键是会设置字节长度的,但凡有字节长度那么就会有上限。
二、MySQL 自增主键用完会怎样
不管我们设置字节长度为多大,如果假设 MySQL 运行时间足够长,那么就一定会用完,对于 MySQL 的情况会分为两种:
1.程序员自己设置的自增主键。
毫无疑问,当数值达到最大时候,再去获取自增主键得到的依然是最大值,插入的时候就会报主键冲突。这个是在 server 层实现的。
2.程序员没有设置自增主键,mysql 自动创建 row_id。
这里需要注意,MySQL 中的 row_id 是在引擎层实现的,InnoDB 代码中会创建一个不可见的长度为 8 的自增字段 row_id,步长为 1,但是 InnoDB 在实现的时候却只给此字段分配 6 个字节的空间长度,因此在保存数据的时候只能取 row_id 字段的最后 6 字节进行保存,我们知道 6 字节数值最大为 2 的 248 次方,如果已经达到这个值后,再次插入数据时候,row_id 就是 2 的 248 次方加 1,从这个数值中取最后 6 字节正好是 0,而在 InnoDB 的实现逻辑中如果 row_id 重复,不会报主键冲突,而是会覆盖原数据。
现在你应该清楚 mysql 的自增主键是有上限的,达到上限后就会出现上面说的现象。
三、mysql 中还有哪些自增 id,达到最大又会如何呢
1.max_trx_id
我们知道 MySQL 中,没创建一个事物就会去申请一个事物 id(trx_id),申请的方式就是从获取全局变量 max_trx_id 当前值,然后将 max_trx_id+1,max_trx_id 是 InnoDB 内部维护的,并且是持久化保存的,也就是说即便 MySQL 重启也不会重置这个值。
一般的 select 语句是不会申请事物 id 的,除非语句后面加上 for update。
max_trx_id 也是 8 个字节的长度,虽然数字足够大,但是假设 mysql 运行时间足够长,早晚也会达到最大值的,max_trx_id 达到最大值后会重置为 0,重新开始。
这种情况下就会有个问题,看下图:
我们知道在可重复读隔离级别下数据的可见性是通过事物的一致性视图来判断的。这种情况下就会出现脏读的 bug。
解释:
假设在上面 sql 执行前系统的 max_trx_id 已经是最大值 999(假设这是最大值),所以在 session A 启动的事务的低水位就是 999。
在 T2 时刻,session B 执行第一条 update 语句的事务 id 就是 999,而第二条 update 语句的事务 id 就是 0 了,这条 update 语句执行后生成的数据版本上的 trx_id 就是 0。
在 T3 时刻,session A 执行 select 语句的时候,判断可见性发现,c=3 这个数据版本的 trx_id,小于 sessionA 的事务低水位,因此认为这个数据可见。
但实际 sessionA 不应该看到 c=3 这条数据,因此出现这个是脏读。
这是 MySQL 必现的一个 bug。
2.thread_id
thread_id 是 MySQL 中常见的一种自增 id,长度为 4 个字节,当达到最大值时就会重置为 0,重新开始,但是我们在日常的维护中用 show processlist 查看的时候从来都不会看到重复的 id,这是因为 MySQL 在实现的时候做了一些操作,代码如下:
因此 MySQL 中的 thread_id 不会出现重复。
mysql 中还有一些其他的自增 id,比如 mysql 中还有 redo log 和 binlog 相关的 xid,binlog 文件序号,还有 table_id 等。但是我们最应该知道就是上面这几个,其他的感兴趣可以随时来找我探讨。
版权声明: 本文为 InfoQ 作者【互联网工科生】的原创文章。
原文链接:【http://xie.infoq.cn/article/179fc3897ecef90e0a44e5339】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论