Mysql 系列 | 自增 ID 用完后
Mysql 中有很多自增 ID
定义了自增 ID 的长度后,就有了最大值,就有可能被用完
表定义自增主键 ID
通过上一篇 Mysql 系列 | 自增 ID 很好理解,主键达到最大值,再申请 ID 时得到的还是原来的自增值,然后主键冲突,数据插入失败。
InnoDB 系统自增 row_id
-
如果表中没有创建主键,在 InnoDB 中会自动创建一个不可见的、长度为 6 字节的 row_id,则范围为 0 ~(2 的 48 次方 - 1)
-
所有没有定义主键的表共用一个全局的 dict_sys.row_id。
-
当申请 (2 的 48 次方)row_id 时,取后 6 位为 0,再循环使用。
-
但是,在 InnoDB 中,取到 row_id=N 时,把 N 插入表中。如果表中已经存在 N 的行,则覆盖前面的行,会导致丢失数据,后果很严重。所以最好手动创建自增主键。
Xid
-
Xid 是 Server 层维护的,用来在 InnoDB 事务和 Server 层之间进行关联,它并不是事务 ID。
-
Mysql 中有个全局变量 global_query_id,每次执行语句时将它赋值给 query_id,然后自己加 1。如果当前语句是事务的第一条语句,同时把 query_id 赋值给事务的 Xid。
-
global_query_id 是个内存变量,重启后会清零。因此同一个数据库实例中,Xid 有可能相同。
-
Mysql 重启后会生成新的 binlog 文件,可以保证同一个 binlog 中的 Xid 不会重复。
-
global_query_id 是 8 个字节,最大值为(2 的 64 次方 - 1),达到最大值后会从 0 开始。所以极端情况下,同一个 binlog 中还是有可能出现同一个 Xid。8 个字节足够大,通常情况下达到最大值只存在于理论中。
事务 trx_id
-
InnoDB 内部维护了一个全局变量 max_trx_id,申请一个新的事务 ID 时,把 max_trx_id 赋值给 trx_id,然后自己加一。
-
InnoDB 数据可见性的思想
-
每一行数据都记录了更新它的 trx_id,当事务读到一行数据时,通过事务的一致性视图和 trx_id 作对比。小于 trx_id 时,判断为可见。
-
正在执行的事务 ID 是 information_schema.innodb_trx 表中的 trx_id
-
-
对于只读事务,InnoDB 并不会分配 trx_id,查到的一个只用来显示的很大的数字,直到事务中出现更新操作才会有真正的事务 ID
-
max_trx_id 是持久化存储的,只要 Mysql 服务跑的足够久,就可能出现达到上限(2 的 48 次方 - 1),然后再从 0 开始。
-
结合数据可见性思想,隔离级别为默认的可重复读。当 trx_id 从 0 开始后,所有的查询都会出现脏读。而且重启也不会改变。
线程 thread_id
-
系统保存了一个全局变量 thread_id_counter,新建一个连接就会把 thread_id_counter 赋值给当前的线程 ID,然后自己加一
-
thread_id_counter 的长度为 4 个字节,达到最大值(2 的 32 次方 - 1)后,从 0 开始。但是不会再
show processlist
中看到相同的 thread_id -
对于线程 ID,mysql 设计了唯一数组的逻辑
do { new_id= thread_id_counter++; } while (!thread_ids.insert_unique(new_id).second);
--- 日常开发中,我们最常见到的就是表主键 ID 用完的情况
上文中了解到如果没有定义主键就会用 row_id,此时会有丢数据的风险
所以我们最好给每张表主动创建主键