39 | MySQL的id自增主键一定都是连续的?(写的不错~)

一、前言

先上一个表结构,后面例子就是依赖这个表数据


CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB;

二、正文

2.1.自增主键保存在哪里?

  • MyISAM 引擎的自增值保存在数据文件中。

  • InnoDB 引擎的自增值。1)在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化。2) MySQL 8.0 版本后,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值。

2.2.自增主键的自增原理是什么,有没有特殊情况?

  • 主要是由两个参数控制:从 auto_increment_offset 开始,以 auto_increment_increment 为步长,持续叠加,直到找到第一个大于 X 的值,作为新的自增值。

  • 双M多库下的自增主键值,最好分开设置不同,主偶备基

2.3.什么情况自增主键会出现不连续(举例)?

  • 现有表数据:row=(1,1,1),insert into t values(null, 1, 1)。唯一键冲突后自增主键id值变为了3。

  • 事务回滚也会出现自增id不连续

2.4.自增id不连续可不可以回退?为什么MySQL这么自增方式这么设计(举例说明这么设计的原因)?解决第二个问题的思路是什么?

  • 不可以

  • 从性能考虑。举例:假设有两个并行执行的事务,在申请自增值的时候,为了避免两个事务申请到相同的自增 id,肯定要加锁,然后顺序申请。假设事务 A 申请到 id=2, 事务 B 申请到 id=3。事务 B 正确提交了,但事务 A 出现了唯一键冲突把自增 id 回退为2,那么现在表里面已经有 id=3 的行,而当前的自增 id 值是 2。其他事务执行就会申请到 id=2,再申请到 id=3。这时,就会出现插入语句报错“主键冲突”。

  • 1)每次申请自增id值时都遍历主键索引拿最大。2)把自增 id 的锁范围扩大,必须等到一个事务执行完成并提交,下一个事务才能再申请自增 id。

2.5.MySQL自增锁的优化是什么样的?

  • MySQL 5.0 版本的时候,自增锁的范围是语句级别。一个语句申请了一个表自增锁,这个锁会等语句执行结束以后才释放

  • MySQL 5.1.22 新增参数 innodb_autoinc_lock_mode,总共有三个值:0,1(默认),2。

  • y=2的x-1次方增长

2.5.1.参数说明

  • 参数的值0 时,表示采用之前 MySQL 5.0 版本的策略

  • 参数的值1 时:普通 insert 语句,自增锁在申请之后就马上释放;类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放

  • 这个参数的值被设置为 2 时,所有的申请自增主键的动作都是申请后就释放锁

2.5.2.insert … select 要使用语句级的锁,为什么这个参数的默认值不是 2?举例说明?如何设置insert...select的参数值?

  • 当binlog_format=statement模式下,下面的场景会出现数据一致性问题。因为:事务在记录binlog的时候,是不能被打断的。基于这个前提,如果 binlog_format=statement,从库执行主库的binlog的时候,sessionb对应的语句,执行出来的数据,id是连续的, 和主库不一致。

  • 从并发插入数据性能的角度考虑设置:innodb_autoinc_lock_mode=2 ,并且 binlog_format=row

2.6.针对insert...select...、replace … select 和 load data 语句MySQL采取的优化是什么?

  • 采取优化的原因是:“不知道要预先申请多少个 id”,如果插入一次申请一次性能上不去。那么MySQL如何解决呢?第一次申请自增id会分配1个,第二次2个,第三次4个,依次类推

三、思考

执行 insert into t2(c,d) select c,d from t; 这个语句的时候,如果隔离级别是可重复读(repeatable read),binlog_format=statement。这个语句会对表 t 的所有记录和间隙加锁。你觉得为什么需要这么做呢?

答:主备数据不一致问题,如果不加,同时有插入语句执行,因为隔离级别是RR,会导致insert select语句不会取到新插入数据,直接插入的语句先记录到binlog中,在备库执行时,insert select又能取到,会出现数据不一致

四、评论区

4.1.一个有意思的问题

:获取自增id和写binlog是有先后顺序的。那么在binlog为statement的情况下。语句A先获取id=1,然后B获取id=2,接着B提交,写binlog,再A写binlog。这个时候如果binlog重放,是不是会发生B的id为1,而A的id为2的不一致的情况?

:不会,因为binlog在记录这种带自增值的语句之前,会在前面多一句,用于指定“接下来这个语句要需要的 自增ID值是多少”,而这个值,是在主库上这一行插入成功后对应的自增值,所以是一致的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值