自增id

自增 id :定义初始值,然后不停往上加步长。其上限取决于字节长度,如无符号整型 (unsigned int) 是 4 个字节,id上限就是 2^32-1。

表定义自增值id上限

1、表定义的自增值达到上限后的逻辑是:再申请下一个 id 时,得到的值保持不变。
2、例子:

create table t(id int unsigned auto_increment primary key) auto_increment=4294967295;
insert into t values(null);
//成功插入一行 4294967295
show create table t;
/* CREATE TABLE `t` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4294967295;
*/

insert into t values(null);
//Duplicate entry '4294967295' for key 'PRIMARY'
//第一个 insert 语句插入数据成功后,这个表的 AUTO_INCREMENT 没有改变(还是 4294967295),就导致了第二个 insert 语句又拿到相同的自增 id 值,再试图执行插入语句,报主键冲突错误。

3、应该创建成 8 个字节的 bigint unsigned。4字节无符号42亿,有符号21亿,除非删除极其频繁,不然不可能用完,因为单表存储最多也就千万级,超过时就要分库分表了,而存储记录达千万(上限1亿吧),自增id达21亿时,说明删除了20亿(其它原因导致id增加的可能比较小),新增1条就会删除20条,这种场景极其罕见。

InnoDB 系统自增 row_id

1、InnoDB 表不指定主键时会创建一个不可见的长度为 6 个字节的 row_id。
2、特征:
row_id 写入表中的值范围,是从 0 到 2^48-1;
当 dict_sys.row_id=2^48时,如果再有插入数据的行为要来申请 row_id,拿到以后再取最后 6 个字节的话就是 0,然后继续循环。如果表中已经存在 row_id=N 的行,新写入的行就会覆盖原有的行。
3、应该在 InnoDB 表中主动创建自增主键。

Xid

1、MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。
2、global_query_id 定义的长度是 8 个字节,这个自增值的上限是 2^64-1,基本等于说不可能到达上限。

Innodb trx_id

1、InnoDB 内部维护了一个 max_trx_id 全局变量,每次需要申请一个新的 trx_id 时,就获得 max_trx_id 的当前值,然后并将 max_trx_id 加 1。
2、InnoDB 数据可见性的核心思想是:每一行数据都记录了更新它的 trx_id,当一个事务读到一行数据的时候,判断这个数据是否可见的方法,就是通过事务的一致性视图与这行数据的 trx_id 做对比。
3、算法:把当前事务的 trx 变量的指针地址转成整数,再加上 2^48。
可以保证以下两点:因为同一个只读事务在执行期间,它的指针地址是不会变的,所以不论是在 innodb_trx 还是在 innodb_locks 表里,同一个只读事务查出来的 trx_id 就会是一样的。如果有并行的多个只读事务,每个事务的 trx 变量的指针地址肯定不同。这样,不同的并发只读事务,查出来的 trx_id 就是不同的。

thread_id

1、系统保存了一个全局变量 thread_id_counter,每新建一个连接,就将 thread_id_counter 赋值给这个新连接的线程变量。
2、thread_id_counter 定义的大小是 4 个字节,因此达到 232-1 后,它就会重置为 0,然后继续增加。
3、为什么不会出现重复的thread_id

do {
  new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);

总结

1、表的自增 id 达到上限后,再申请时它的值就不会改变,进而导致继续插入数据时报主键冲突的错误。
2、row_id 达到上限后,则会归 0 再重新递增,如果出现相同的 row_id,后写的数据会覆盖之前的数据。
3、Xid 只需要不在同一个 binlog 文件中出现重复值即可。虽然理论上会出现重复值,但是概率极小,可以忽略不计。
4、InnoDB 的 max_trx_id 递增值每次 MySQL 重启都会被保存起来,所以我们文章中提到的脏读的例子就是一个必现的 bug,但基本不会出现。
5、thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了。


ciwi
1 声望0 粉丝