Description
表定义自增值 id
表定义的自增值达到上限之后,再申请下一个 id 时,得到的值保持不变
InnoDB 系统自增 row_id
InnoDB 维护了一个全局的 dict_sys.row_id 值,所有无主键的 InnoDB 表,每插入一行数据,都将当前的 dict_sys.row_id 值作为要插入数据的 row_id,然后把 dict_sys.row_id 的值加 1
实际上,在代码实现时 row_id 是一个长度为 8 字节的无符号长整型 (bigint unsigned)。但是,InnoDB 在设计时,给 row_id 留的只是 6 个字节的长度,这样写到数据表中时只放了最后 6 个字节,所以 row_id 能写到数据表中的值,就有两个特征:
- row_id 写入表中的值范围,是从 0 到 248-1;
- 当 dict_sys.row_id=248时,如果再有插入数据的行为要来申请 row_id,拿到以后再取最后 6 个字节的话就是 0。
也就是说,写入表的 row_id 是从 0 开始到 248-1。达到上限后,下一个值就是 0,然后继续循环。而这样的话,超过上下后就会覆盖之前的数据
Xid
MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid
global_query_id 是纯内存变量,重启之后清零,但 MySQL 重启之后也会重新生成新的 binlog 文件,这里也保证了同一个 binlog 文件里 Xid 一定是唯一的
不过 global_query_id 达到上限后,也会从 0 开始计数,但 global_query_id 定义的长度为 8 个字节,这个值很大,可能性微乎其微
Innodb trx_id
InnoDB 内部维护了一个 max_trx_id 全局变量,每次需要申请一个新的 trx_id 时,就获得 max_trx_id 的当前值,然后将 max_trx_id 加 1,max_trx_id 会持久化存储,重启也不会重置为 0,理论上,只要 MySQL 服务运行的足够久,就会出现 max_trx_id 重新从 0 开始的情况,当达到这样一个状态时,就会持续出现脏读的 bug
thread_id
MySQL 保存了一个全局变量 thread_id_counter,每新建一个连接,就将 thread_id_counter 复制给这个新连接的线程变量。
thread_id_counter 定义的大小是 4 个字节,因此达到 232-1 后,它就会重置为 0,然后继续增加
MySQL 在给新线程分配 thread_id 的时候,有如下逻辑:
do {
new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);