暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

什么?MySQL唯一索引还会有重复值?

121

昨天,一位【DBA驿站】星友在工作中遇到了这样一个问题:

一主两从的架构,某个字段有唯一索引。

主库和从1库 插入了一条相同值的唯一索引数据,插入成功了;

但是从2库就报错了,报唯一索引冲突。

表结构浓缩版如下:

    `trade_no` varchar(64DEFAULT NULL,
    UNIQUE KEY `trade_no_idx` (`trade_no`) USING BTREE,

    大家看到这里,可以先思考一下,可能哪些原因会导致这种情况的出现?


    我再来写写自己的看法。

    在去年8月,就有一位DBA体系课学员出去面试,被问到过类似的问题:

    有唯一索引还有重复数据是怎么回事?

    这个我们当时也在DBA驿站内解析过:

    https://t.zsxq.com/HgF0k


    其实在那之前我也没遇到过,不过网上找到了一些资料,加上星球内星友的讨论,我们总结出3种可能:

    第一种:unique_checks=0的时候

    有好几篇文章详细讨论过这个问题。

    爱可生的这篇文章:

    故障分析 | MySQL:唯一键约束失效

    八怪老师写的:

    MySQL:Innodb唯一索引出现重复值的场景分析

    老叶茶馆的文章:

    故障案例:MySQL唯一索引有重复值,官方却说This is not a bug


    大概就是说:

    unique_checks=0的时候,即使涉及到唯一索引的操作,DML 操作的数据如果不在 innodb buffer pool 中,也不会通过读取磁盘上的主键、唯一索引进行唯一检测,此时就和普通的二级索引一样,直接在 change buffer 完成数据写入,目的是想减少磁盘 IO,但这种情况下,可能就会导致唯一索引的字段有重复数据。

    昨天这位星友遇到的问题,大概率就是这个原因。


    第二种:RR隔离级别

    大家可以做个实验:

      CREATE TABLE `martin` (
        `id` int NOT NULL AUTO_INCREMENT,
        `a` int DEFAULT NULL,
        `b` int DEFAULT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `uniq_a` (`a`)
      ) ENGINE=InnoDB;
      insert into martin(a,b) values(1,1),(2,2);


      session1

        begin;
        select * from martin;

        session2

          update martin set a=3 where id=1;

          session1

            update martin set a=1 where id=2;
            select * from martin;


            确实看到有唯一索引的a字段出现了重复值:

            为什么会出现这种情况?

            对于id=1的记录,即使session2更新了,但是RR隔离级别下,在同一个事务里相同的查询,并不能看到其他事务的更新。

            为什么session1把id=2的行a更新成1,不会报唯一键冲突?

            因为RR隔离级别中,更新的时候,实际是当前读,他知道session2已经把id=1中的a改成了3,没有a=1的记录,自然也不会报冲突了。


            其实这个时候,session1事务还没提交,提交之后再看,就是合理的值了:


            第三种情况:唯一索引字段多行包含null

            这个是当时在【DBA驿站】中讨论,有星友提出的。

            当然,这种情况,很多时候默认允许的,但是得注意业务逻辑。

            比如一个条件是唯一索引字段,类似的SQL是:

              SELECT id FROM t WHERE a = ?;

              有唯一约束,代码层就只会当一行进行处理,但出现null,可能就会有多行返回,就会导致程序报错。我们得注意这一点。


              能想到的就这些情况了,有其他的也欢迎大家补充。


              对了,也欢迎大家加入我们的DBA付费社群《DBA驿站》。

              目前已经有300多位成员,这里面,还有不少大厂的DBA。比如网易、阿里、京东、字节等互联网大厂的朋友。

              大家可以在里面交流技术、分享经验。

              感兴趣的可以领取下方优惠码加入(券后78¥)。

              老用户,目前续费是5折。

              考虑系统学习DBA的,可以执行这条SQL试试:

                SELECT
                'DBA体系课' AS 课程名称,
                1999 AS 原价,
                1249 AS 618活动价,
                CONCAT('省'1999 - 1249) AS 节省金额,
                'https://class.imooc.com/sale/dba' AS 课程地址,
                '联系mating3306获取优惠码' AS 活动参与方式;


                文章转载自MySQL数据库联盟,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论