昨天,一位【DBA驿站】星友在工作中遇到了这样一个问题:
一主两从的架构,某个字段有唯一索引。
主库和从1库 插入了一条相同值的唯一索引数据,插入成功了;
但是从2库就报错了,报唯一索引冲突。
表结构浓缩版如下:
`trade_no` varchar(64) DEFAULT NULL,UNIQUE KEY `trade_no_idx` (`trade_no`) USING BTREE,
大家看到这里,可以先思考一下,可能哪些原因会导致这种情况的出现?
我再来写写自己的看法。
在去年8月,就有一位DBA体系课学员出去面试,被问到过类似的问题:
有唯一索引还有重复数据是怎么回事?
这个我们当时也在DBA驿站内解析过:
https://t.zsxq.com/HgF0k
其实在那之前我也没遇到过,不过网上找到了一些资料,加上星球内星友的讨论,我们总结出3种可能:
第一种:unique_checks=0的时候
有好几篇文章详细讨论过这个问题。
爱可生的这篇文章:
八怪老师写的:
老叶茶馆的文章:
故障案例: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 活动参与方式;




