线上MySQL的自增id用尽怎么办?
![](https://filescdn.proginn.com/42f9e0b95a46276ec2b394f5ba4f36f8/52726bda926e97fea738fa140f12babb.webp)
表定义自增值id
表定义的自增值达到上限后的逻辑是:再申请下一个id时,得到的值保持不变。
mysql> create table t(id int unsigned auto_increment primary key) auto_increment=4294967295;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t values(null);
Query OK, 1 row affected (0.00 sec)
mysql> show create table t;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t | CREATE TABLE `t` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4294967295 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
//成功插入一行 4294967295
mysql> insert into t values(null);
ERROR 1062 (23000): Duplicate entry '4294967295' for key 't.PRIMARY'
第一个insert成功后,该表的AUTO_INCREMENT还是4294967295,导致第二个insert又拿到相同自增id值,再试图执行插入语句,主键冲突。
2^32 - 1(4294967295)不是一个特别大的数,一个频繁插入删除数据的表是可能用完的。建表时就需要考虑你的表是否有可能达到该上限,若有,就应创建成8字节的bigint unsigned。
InnoDB系统自增row_id
若你创建的InnoDB表未指定主键,则InnoDB会自动创建一个不可见的,6个字节的row_id。InnoDB维护了一个全局的dict_sys->row_id值
![](https://filescdn.proginn.com/66f1dee96507c9160f52c668fec204fc/275a571924f6f102f1b0b8e5f7c3a8c2.webp)
row_id用完的验证序列
![](https://filescdn.proginn.com/1f9d5ec9769ec8a5439ef15ddb1c6fa6/d71b2319527ef50ee452cf58f8a09e16.webp)
row_id用完的效果验证
Xid
![](https://filescdn.proginn.com/1815e73a0b00e49c1675cbd70fb3c2b9/6e41a10be6a062b2fc845c561836fde8.webp)
每次执行语句时,将它赋值给query_id,然后给该变量+1:
![](https://filescdn.proginn.com/1573f322c82030a9d8a1770455ebda13/2199818b22250f71af1db984a51016c5.webp)
若当前语句是该事务执行的第一条语句,则MySQL还会同时把query_id赋值给该事务的Xid:
![](https://filescdn.proginn.com/16cffb78129867bc693563daabd404b3/3b3db06626c736ac442ca83ecb51bb12.webp)
Innodb trx_id
Xid由server层维护
InnoDB数据可见性的核心思想
![](https://filescdn.proginn.com/209bfb43122a48f78ccb277851443da6/e09b3e9f417ed01e81748bbeb7126b83.webp)
mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select trx_id, trx_mysql_thread_id from innodb_trx;
+-----------------+---------------------+
| trx_id | trx_mysql_thread_id |
+-----------------+---------------------+
| 421972504382792 | 70 |
+-----------------+---------------------+
1 row in set (0.00 sec)
mysql> select trx_id, trx_mysql_thread_id from innodb_trx;
+---------+---------------------+
| trx_id | trx_mysql_thread_id |
+---------+---------------------+
| 1355623 | 70 |
+---------+---------------------+
1 row in set (0.01 sec)
t1时,trx_id的值就是0。而这个很大的数,只是显示用 直到S1在t3时执行insert,InnoDB才真正分配trx_id。所以t4时,S2查到该trx_id的值就是1289。
update 和 delete语句除了事务本身,还涉及到标记删除旧数据,即要把数据放到purge队列里等待后续物理删除,这个操作也会把max_trx_id+1, 因此在一个事务中至少加2 InnoDB的后台操作,比如表的索引信息统计这类操作,也是会启动内部事务的,因此你可能看到,trx_id值并不是按照加1递增的。
t2时查到的很大数字是怎么来的?
每次查询时,由系统临时计算:
当前事务的trx变量的指针地址转成整数,再加上248
这样可以保证:
因为同一只读事务在执行期间,它的指针地址不会变,所以无论在 innodb_trx还是在innodb_locks表里,同一个只读事务查出来的trx_id就会是一样的 若有并行只读事务,每个事务的trx变量的指针地址肯定不同。这样,不同并发只读事务,查出来的trx_id就是不同的。
为什么要加248?
为何只读事务不分配trx_id?
减小事务视图里面活跃事务数组的大小。因为当前正在运行的只读事务,不影响数据的可见性判断。所以,在创建事务的一致性视图时,InnoDB就只需要拷贝读写事务的trx_id
减少trx_id的申请次数。InnoDB执行一个普通的select语句,也要对应一个只读事务。所以只读事务优化后,普通查询语句无需申请trx_id,大大减少并发事务申请trx_id的锁冲突
![](https://filescdn.proginn.com/acd7493d4a5610002f426c1bd11364b5/0824d17624fdc29f25adad1fb9b7d8c9.webp)
![](https://filescdn.proginn.com/43538323d16b798d11dbccdb101bdb6a/acff6e7f3002108117241363c822831f.webp)
因为系统的max_trx_id被设置成2^48 - 1,所以在session A启动的事务TA的低水位就是2^48 - 1。
t2时:
session B执行第一条update语句的事务id=2^48 - 1
第二条事务id就是0了,这条update执行后生成的数据版本上的trx_id=0
t3时:
thread_id
系统保存了一个全局变量thread_id_counter
![](https://filescdn.proginn.com/4c67dd5c90beba1ceaeb6324272d29dc/edd866bfee7b10410c9677e5a8ba4265.webp)
![](https://filescdn.proginn.com/945f2a7b8bc085e0119bb8316ff4d1ce/9e45d32f93792e4c6df5896a8d8a8ded.webp)
![](https://filescdn.proginn.com/4167e0130bafe88c7803cb693a8db0ea/a2d31f112caa53e4b53ecd595cc5840b.webp)
给新线程分配thread_id时的逻辑:
![](https://filescdn.proginn.com/c42c231b488ab5b16bbffb3833883a92/746c6eae66fd8af725876479cec699f9.webp)
总结
评论