mysql的自增id用完怎么办?
前言
读书笔记《mysql45讲》第45讲
mysql的自增id用完怎么办?
- 前言
- 其实id并没有上限,有上限的是字段类型。
- 表的自增id
- innodb的row_id
- mysql的service层的xid
- innodb的trx_id
- 对于读事务。
- 对于写事务
- 线程ID threadId
其实id并没有上限,有上限的是字段类型。
按道理来说id可以一直增加,但是id字段是什么类型的,每个类型一般是有上限的。比如int类型,在非负数的情况下是 2的23次方-1。
表的自增id
表的自增id如果使用的是int类型,是4字节就是2的32次方-1。如果达到之后,下一个数据进来之后仍然会给它2的32次方-1这个值,那么在插入数据的时候就会报 主键冲突的问题。
解决方案:可以通过使用更大的类型 bigint就是2的64次方-1了。
innodb的row_id
如果你没有申请主键,innodb会自动添加一个不可见的row_id做数据的区分。这个使用的是8字节bigint类型,但是实际只写到了6字节。就是2的48次方。当达到最大值之后,就会从0继续开始。那么就会覆盖之前row_id=0的数据。
如果一个系统运行足够久还是有可能达到的。
mysql的service层的xid
xid是事务的唯一id,在之前讲解redolog和binlog互配合的时候,它们有一个共同的字段就是xid。
它是怎么生成的呢,mysql中维护了一个global_query_id在内存中,当服务重启时,就会清空从0开始,但是binlog也会清空。所以可以保证在同一个binlog文件中的xid是不同的。
这个值是8字节也就是2的64次方是一个非常大的值。理论上不会达到出现在一个文件中。因此可以忽略。
innodb的trx_id
trx_id和xid容易混淆,xid是mysql的service层声明的,innodb中使用xid就是为了和service关联起来。trx_id是innodb自己维护的,这个是之前学习事务到底隔离还是不隔离时使用的,用来保证事务的一致性视图,那些事务可以展示那些不可以展示。
长度是2的64次方。达到最大值之后也是会从0继续开始计算。
但是这个记录当前执行到多少的max_trx_id会持久化存储,也就是重启之后也不会丢失,因此只要一个系统运行时间足够久,就一定会出现这个问题,这个问题就是脏读。
就是一个刚刚更新的事务的trx_id=0,那么其它事务的trx_id都比他大就会导致立即可见,破坏了可重复读,但是这个问题由于需要达到2的64次方是一个非常大的数据,目前还没有那个程序出现。tps=50w/秒,也需要17.5年左右。
对于读事务。
其实并不会真正给分配一个trx_id因为并不会影响到一致性视图。读请求看到的trx_id一般是通过事务的指针地址转换成整数再加上2的48次方.
之所以加上2的48次方是为了减少这个trx_id和其它写事务冲突的情况。
同时使用了指针地址是因为并行查询的事务指针地址肯定不同,就保证了同一时刻的读事务trx_id不会冲突。
那么不给读请求分配trx_id有什么好处,大大减小了trx_ID的自增速度。减少活跃事务里面的trx_id的数量,在判断一个事务都有哪些事务可见时,其实读请求并不会破坏一致性视图,所以只需要拷贝这些写操作的视图判断即可。
对于写事务
那么对于写事务看到的也不是连续的,这是因为,一个更新操作除了事务本身,还涉及到标记旧数据,把数据放到purge队列中等待删除。这个过程也会使得trx_id+1。
除此之外,还有一些内部的事务,如表的索引信息统计。等也会使得trx_id+1。
线程ID threadId
threadId的大小时2的32次方-1,到达最大值之后就会重置从0开始。但是不会出现冲突,下面是它的处理逻辑:
do {new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);
这是线程的代码逻辑,有新增就+1,但是插入的时候会判断一下是否有这个值了,如果有了就会返回false,然后再+1。保证不会出现重复,相对于其他几种方式 非常的优雅。