Mysql中有很多个自增id,每个自增id都是有初始值的,虽然自然数是没有上限的,不过要定义了它的字节长度那就是有上限的,如无符号整形(unsigned int)是4个字节,其上限就是2^32 - 1。
表定义的自增id:
自增id应该先想到的就是表定义的自增id;表定义的自增id达到上限后的逻辑是:再申请下一个id时保持不变,可以通过下面方式验证:
/* CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4294967295;
*/ //创建一个id从最大字节的数值开始自增的测试表
此时插入一行数据提示://Duplicate entry '4294967295' for key 'PRIMARY'
上面实验可以看出对于一个频繁插入/删除的表,达到上限还是有可能达到这个上限的值。如果会出现需要创建成8个字节的无符号整形
InnoDB系统自增row_id:
如果创建的表没有指定自增id,那么系统会为表指定一个row_id,长度为6个字节,由InnoDB指定,InnoDB维护了一个全局的dict_sys.row_id, 当没有指定自增主键的表新增一条数据时,会将此row_id的值赋值给要插入数据的row_id,再将dict_sys.row_id的值加1;
实际上row_id是8个字节,但InnoDB在设计时为row_id留了6个字节,所以后面6个字节表示能写到数据表的row_id的值,具备以下两个特征:
1、row_id的范围是0 - 2^48-1
2、达到上限之后会从0开始 本身 2^48-1是一个很大的数,如果mysql实例本身跑的时间很长就会出现这样的情况
所以循环到0的时候后写入的数据就会覆盖之前写入的数据,具体测试可以用gdb修改系统的dict_sys.row_id的值,要测试的话一定在测试库试一下
综上建议还是在创建数据表的时候指定一下自增id这样在数据插入达到上限的时候会提示主键冲突
XID
redo_log和bin_log在相配合的时候的一个共同的字段Xid,是用来对应事务的,Mysql内部维护了一个全局变量global_query_id在执行语句时,会把这个值赋值给Query_id,然后再将这个值加1操作,如果当前语句是这个事务的第一条语句则会将Query_id再赋值给XID
一个数据库实例中不同事务的XID也可能是相同的,是因为global_query_id是一个纯内存的变量,重启之后会置0,不过重启之后就会生成一个新的binlog,这就保证了一个binlog文件中Xid的值是唯一的,但是如果global_query_id达到上限时,也会出现一个binlog文件中出现两个相同的Xid,global_query_id的上限是2^64-1,要出现这种情况必须:
1、开启一个事务,2、执行2^64次查询语句,3、再开启一个新的事。 这样就会出现上述情况,不过2^64还是一个很大的值,所以那种情况出现也只是理论上。
InnoDB trx_id
Xid和trx_id的区别是Xid是维护在server层,InnoDB使用Xid是为了能够在InnoDB事务和server之间做关联,InnoDB内部的trx_id是另外维护的,在开启事务时会申请一个新的trx_id,InnoDB内部维护了一个max_trx_id,当新事务申请trx_id时会将max_trx_id赋值给trx_id,再将max_trx_id加1操作
InnoDB的数据可见行核心思想是:每一行数据都记录了更新和操作它的trx_id,当一个事务读到一行数据时判断这个数据的可见行的方法是 通过事务的一致性视图与这行的trx_id相比较;
trx_id和row_id类似最大值是2^48,但是max_trx_id会持久化存储,重启也不会置0,所以如果一个数据库服务跑的足够久还是会出现的;如果达到上限然后下一次继续从0开始计数。重新计数时就会出现脏读(即trx_id应处于高水位由于这个原因此trx_id处于低水位 故如果某一事务的低水位大于此trx_id则可以看到该数据)。此后的数据都会出现脏读
thread_id
thread_id是一种常见的自增id,系统保存了一个全局变量thread_id_counter,每新建一个连接就将此值赋值给新连接的线程变量,大小是4个字节;达到上限之后会置0,不过show processlist不会出现两个相同的thread_id,原因是:mysql设计了一个唯一数组的概念 如下:
do {
new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);总结:
1、表自增ID达到上限:指定自增ID时会提示主键冲突
2、row_id达到上限:会归0,后写的数据会覆盖之前的数据
3、Xid达到上限:理论上不会出现,只要一个binlog里面不会出现即可
4、trx_id达到上限:由于每次重启都会持久化,所以还是会出现到上限,不过还得好久才必现的bug吧。。。
5、thread_id达到上限:比较常见mysql处理的很好
参考自:林晓斌<<mysql45讲>>