至于模式2,什么情况都不加AUTO_INC锁,存在安全问题,当binlog格式设置为Statement模式的时候,从库同步的时候,执行结果可能跟主库不一致,问题很大。因为可能有一个复杂插入,还在执行呢,另外一个插入就来了,恢复的时候是一条条来执行的,就不能重现这种并发问题,导致记录id可能对不上。
至此,id跳跃的问题算是分析完了,由于innodb_autoinc_lock_mode值是1,INSERT ... ON DUPLICATE KEY UPDATE ...是简单的语句,预先就可以计算出影响的行数,所以不管是否更新,这里都将auto_increment加1(多行的话大于1)。
如果将innodb_autoinc_lock_mode值改为0,再次执行INSERT ... ON DUPLICATE KEY UPDATE ...的话,你会发现auto_increment并没有增加,因为这种模式直接加了AUTO_INC锁,执行完语句的时候释放,发现没有增加行数的话,不会增加自增id的。

INSERT ... ON DUPLICATE KEY UPDATE ...影响的行数是1为什么返回2?

为什么会这样呢,按理说影响行数就是1啊,看看官方文档的说明
With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values
官方明确说明了,插入影响1行,更新影响2行,0的话就是存在且更新前后值一样。是不是很不好理解?
其实,你要这样想就好了,这是为了区分到底是插入了还是更新了,返回1表示插入成功,2表示更新成功。

解决方案

将innodb_autoinc_lock_mode设置为0肯定可以解决问题,但这样的话,插入的并发性可能会受很大影响,因此小A自己想着DBA也不会同意。经过考虑,目前准备了两种较为可能的解决方案:

修改业务逻辑

修改业务逻辑,将INSERT ... ON DUPLICATE KEY UPDATE ...语句拆开,先去查询,然后去更新,这样就可以保证主键不会不受控制的增大,但增加了复杂性,原来的一次请求可能变为两次,先查询有没有,然后去更新。

删除表的自增主键

删除自增主键,让唯一索引来做主键,这样子基本不用做什么变动,只要确定目前的自增主键没有实际的用处即可,这样的话,插入删除的时候可能会影响效率,但对于查询多的情况来说,小A比较两种之后更愿意选择后者。

结语

其实INSERT ... ON DUPLICATE KEY UPDATE ...这个影响行数是2的,小A很早就发现了,只是没有保持好奇心,不以为然罢了,没有深究其中的问题,这深究就起来会带出来一大串新知识,挺好,看来小A还是要对外界保持好奇心,保持敏感,这样才会有进步。