深入探寻mysql自增列导致主键重复问题的原因

 更新时间:2014年08月17日 09:30:26   投稿:hebedich  
前几天开发的同事反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下。以免以后有童鞋遇到类似问题百思不得其解,难以入眠,哈哈。

废话少说,进入正题。

     拿到问题后,首先查看现场,发现问题表的中记录的最大值比自增列的值要大,那么很明显,当有记录进行插入时,自增列产生的值就有可能与已有的记录主键冲突,导致出错。首先想办法解决问题,通过人工调大自增列的值,保证大于表内已有的主键即可,调整后,导数据正常。问题是解决了,接下来要搞清楚问题原因,什么操作导致了这种现象的发生呢?

      这里有一种可能,即业务逻辑包含更新自增主键的代码,由于mysql的update动作不会同时更新自增列值,若更新主键值比自增列大,也会导致上述现象:记录最大值比自增主键值大。但开发反馈说这张表仅仅存在load data infile操作,不会进行更新主键操作,所以这个解释行不通。继续分析,表中含有唯一约束,会不会和唯一约束有关,线下实验模拟没有重现。后来想想会不会和主备切换有关系,因为前两天做过一次主备切换。于是乎,配合主备环境作了测试,果然和主备切换有关系,一切问题的来源都清晰了。

问题发生的前置条件:

       1.mysql复制基于row模式

       2.innodb表

       3.表含有自增主键,并且含有唯一约束

       4.load data infile 采用replace into语法插入数据【遇到重复唯一约束,直接覆盖】

问题发生的原理:

        1.主库遇到重复unique约束时,进行replace操作;

        2.replace在主库上面实际变化为delete+insert,但binlog记录的是update;

        3.备库重做update动作,更新主键,但由于update动作不会更新自增列值,导致更新后记录值大于自增列值

问题重现实验:

准备工作

Create table test_autoinc(id int auto_increment, c1 int,c2 varchar(100),primary key(id),unique key(c1));

insert into test_autoinc(c1,c2) values(1,'abc');

insert into test_autoinc(c1,c2) values(2,'abc');

insert into test_autoinc(c1,c2) values(3,'abcdd');

insert into test_autoinc(c1,c2) values(4,'abcdd');

insert into test_autoinc(c1,c2) values(5,'abcdd');

1

操作

备注

Master

slave

2

查看自增列值

Show create table

test_autoinc\G

插入5条记录后,自增列值变为6

CREATE TABLE `test_autoinc` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `c1` int(11) DEFAULT NULL,

  `c2` varchar(100) DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `c1` (`c1`)

) ENGINE=InnoDBAUTO_INCREMENT=6DEFAULT CHARSET=utf8

CREATE TABLE `test_autoinc` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `c1` int(11) DEFAULT NULL,

  `c2` varchar(100) DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `c1` (`c1`)

) ENGINE=InnoDBAUTO_INCREMENT=6DEFAULT CHARSET=utf8

 

3

查看表数据

 

id | c1   | c2  

---+------+------

 1 |    1 | abc 

 2 |    2 | abc 

 3 |    3 | abcdd

 4 |    4 | abcdd

 5 |    5 | abcdd

id | c1   | c2  

---+------+------

 1 |    1 | abc 

 2 |    2 | abc 

 3 |    3 | abcdd

 4 |    4 | abcdd

 5 |    5 | abcdd

4

查看binlog位置

show master status\G

记录当前binlog位点,

后续可以查看replace动作产生的binlog事件

mysql-bin.000038

59242888

 

5

replace操作

replace into test_autoinc(c1,c2) values(2,'eeee');

影响两条记录,主库replace=

delete+insert

 

Query OK, 2 rows affected

(0.00 sec)

 

 

 

6

查看表数据

 

id | c1   | c2   

---+------+-------

 1 |    1 | abc  

 3 |    3 | abcdd

 4 |    4 | abcdd

 5 |    5 | abcdd

 6 |    2 | eeee 

id | c1   | c2   

---+------+-------

 1 |    1 | abc  

 3 |    3 | abcdd

 4 |    4 | abcdd

 5 |    5 | abcdd

 6 |    2 | eeee 

7

查看binlog事件

show binlog events in 'mysql-bin.000038' from 59242888;

也可以通过mysqlbinlog工具分析日志,查询从库执行的update语句

Pos      | Event_type   

---------+---------------

59242888 | Query        

59242957 | Table_map    

59243013 |Update_rows_v1

59243072 | Xid          

 

8

查看自增列值

Show create table

此时master的自增列为7,而slave的自增列为6,与表内最大值相同

CREATE TABLE `test_autoinc` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `c1` int(11) DEFAULT NULL,

  `c2` varchar(100) DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `c1` (`c1`)

) ENGINE=InnoDBAUTO_INCREMENT=7

CREATE TABLE `test_autoinc` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `c1` int(11) DEFAULT NULL,

  `c2` varchar(100) DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `c1` (`c1`)

) ENGINE=InnoDBAUTO_INCREMENT=6

经过第8步操作后,若发生主备切换,slave提供服务,此时通过自增列插入主键6的记录,就会发生主键冲突。

     如何解决这个bug?对于replace操作,生成binlog时也生成delete和insert两个事件而非一个update事件;或者在执行update更新主键的同时也更新自增列值。当然了,这个只是纯原理分析,具体采用什么方法解这个问题,要根据mysql内部的实现,避免引入新的问题。这个bug我同事已经提交到社区,http://bugs.mysql.com/73563 ,大家可以看看。

相关文章

  • 开启MySQL的binlog日志的方法步骤

    开启MySQL的binlog日志的方法步骤

    这篇文章主要介绍了开启MySQL的binlog日志的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 修改mysql密码与忘记mysql密码的处理方法

    修改mysql密码与忘记mysql密码的处理方法

    修改mysql密码与忘记mysql密码的处理方法,有需要的朋友可以参考下
    2013-02-02
  • 一步步教你利用Mysql存储过程造百万级数据

    一步步教你利用Mysql存储过程造百万级数据

    因工作需要维护一张中建表数据内置,所以得造数据所以使用存储过程来造数据,下面这篇文章主要给大家介绍了关于如何一步步利用Mysql存储过程造百万级数据的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • Mac OS10.11下mysql5.7.12 安装配置方法图文教程

    Mac OS10.11下mysql5.7.12 安装配置方法图文教程

    这篇文章主要为大家详细介绍了Mac OS10.11下mysql5.7.12 安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • MySQL 虚拟列和虚拟索引的实现

    MySQL 虚拟列和虚拟索引的实现

    虚拟列是MySQL 5.7开始引入的新特性,本文主要介绍了MySQL 虚拟列和虚拟索引的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • MySQL主从复制延时问题的解决方案

    MySQL主从复制延时问题的解决方案

    MySQL主从一直是面试常客,里面的知识点虽然基础,但是能回答全的同学不多,如果我们遇到问如何解决MySQL主从复制延时问题呢,所以本文给大家讲解了MySQL主从复制延时问题的解决方法,需要的朋友可以参考下
    2023-12-12
  • mysql免安装版配置步骤详解分享

    mysql免安装版配置步骤详解分享

    这篇文章主要介绍了mysql免安装版配置步骤详解,提供了二个网友的安装方法,大家可以参考使用
    2013-12-12
  • MySQL查询语法汇总

    MySQL查询语法汇总

    这篇文章主要介绍了MySQL查询语法的汇总,帮助大家更好的理解和学习mysql,感兴趣的朋友可以了解下
    2020-08-08
  • vscode安装mysql相关插件的详细步骤

    vscode安装mysql相关插件的详细步骤

    这篇文章主要给大家介绍了关于vscode安装mysql相关插件的详细步骤,这将帮助你更高效地管理数据库操作,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • mysql中insert与select的嵌套使用方法

    mysql中insert与select的嵌套使用方法

    这篇文章主要介绍了mysql中insert与select的嵌套使用方法,代码功能非常实用,需要的朋友可以参考下
    2014-07-07

最新评论