关于自增键的一些问题

几个关于自增键的知识点:(总结自极客时间mysql专栏

  • 自增键在5.7及之前保存在内存里,在8.0开始持久化到redo log
  • 双M架构的自增键要求双写,常采取auto_increment_increment=2,一个奇数一个偶数
  • 自增键不是连续的,有多种原因可产生:唯一键冲突、事务回滚、大规模插入时预判指数型申请自增键
  • 几个相关的变量:
    • auto_increment_offset自增键开始值
    • auto_increment_increment相邻自增键相差值
    • innodb_autoinc_lock_mode:0语句执行结束后放锁;1普通insert语句申请结束后马上释放,insert…select语句结束后释放(以防binlog_format=statement时主从不一致);2申请后就放锁
  • 加自增键不能用8.0的instant ddl feature,因为无法在ddl当下就确定默认值

展开讨论一下:在一主多备结构中,自增键列在复制过程中可能出现数据不一致。

自增键主备不一致

  • 当表里已有自增键时:

当innodb_autoinc_lock_mode=2时采取state replication,每个对auto increment值的申请完成后就放锁,可能出现主备不一致。在master可能出现的情况是,在insert…select这种无法准确预计要申请多少个auto increment值的时候,执行过程中有另一个transaction新的insert语句在同一个表中,那么从insert…select这个语句看来产生的auto_increment值可能不是连续的(因为另一个transaction的insert语句会打破这种连续性)。而因为statement在binlog里只可能先记录insert…select再记录另一个trx,或者先记录另一个trx再记录insert…select,那么在备份中的insert…select语句的auto_increment key肯定是连续的。

可以简单repro一下:

trx1 trx2
insert(id=1)  
  insert(id=2)
insert(id=3)  
  insert(id=4)
commit  
  commit
   

trx1生成的主键是1、3,而trx2生成的主键是2、4。但在commit的时候,trx1写在trx2前面,在从库中trx1会生成1、2,trx2会生成3、4。这就是为什么通常建议innodb_autoinc_lock_mode=2,并采用row replication(行复制会将auto increment值也复制),可增加并发度。

  • 当向表里新加一列自增键时:

如果表里没有主键,因为行在集群的每个mysqld上的排列可能不同,可能导致主备数据不一致。如果表里本身有主键,除非采用了parallel DDL,不会有主备不一致的问题。

这篇文章有详细讲述

References

  • https://dev.mysql.com/doc/refman/8.0/en/replication-features-auto-increment.html
  • https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html