自增主键不能保证连续递增,会有空洞存在。

  1. CREATE TABLE `t` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `c` int(11) DEFAULT NULL,
  4. `d` int(11) DEFAULT NULL,
  5. PRIMARY KEY (`id`),
  6. UNIQUE KEY `c` (`c`)) ENGINE=InnoDB;

导致空洞的情况:

  1. 唯一键冲突导致没有插入成功
  2. 事务回滚
  3. 批量申请id导致部分id浪费

空洞的原因:

自增值的修改,是在SQL真正执行之前的。如果SQL最终没有执行,自增值不能回退。因为自增值回退的话,会带来很多问题。允许回退的话,那要么保证申请id前此id在表中不存在,要么自增id的锁粒度扩大申请退化成串行,都会严重影响性能。

MySQL是不知道需要预先申请多少个自增id的,当批量插入时MySQL批量申请自增id的策略:

  1. 第一次申请自增id,分配1个;
  2. 1个用完后,这个语句第二次申请,分配2个;
  3. 上面的2个用完后,这个语句第三次申请,分配4个。也就是每次申请时上一次的2倍。

    自增主键的唯一性

    自增步长和自增所控制并发。auto_increment_offset代表自增初始值,auto_increment_increment代表步长,默认都是1。
    如果想要自增id都是奇数或者都是偶数,可以借助初始值,并将步长设置为2,来达到。比如双M的主备结构里要求双写时,需要让一个库自增id都是奇数,另一个库都是偶数,来保证两个库的主键不冲突。

    自增id达到上限

    每种自增 id 有各自的应用场景,在达到上限后的表现也不同:

  4. 表的自增 id 达到上限后,再申请时它的值就不会改变,进而导致继续插入数据时报主键冲突的错误。

  5. row_id 达到上限后,则会归 0 再重新递增,如果出现相同的 row_id,后写的数据会覆盖之前的数据。
  6. Xid 只需要不在同一个 binlog 文件中出现重复值即可。虽然理论上会出现重复值,但是概率极小,可以忽略不计
  7. 。InnoDB 的 max_trx_id 递增值每次 MySQL 重启都会被保存起来,所以我们文章中提到的脏读的例子就是一个必现的 bug,好在留给我们的时间还很充裕。
  8. thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了。