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,此时会有丢数据的风险

所以我们最好给每张表主动创建主键

posted @ 2022-09-02 11:06  菜乌  阅读(167)  评论(0编辑  收藏  举报