一个关于MySQL唯一索引的注意点

278 阅读2分钟

谨言慎行,友善交流

背景

有个业务场景依赖mysql的唯一索引进行防重,于是在涉及的字段上创建了一个联合唯一索引。某一天我们收到了业务告警,定位排查后发现是因为数据库里面有一条“重复的”数据。

1、问题

-- 创建一个测试表,在company和city上添加唯一索引
create table test_unique_key(
    id int primary key,
    company varchar(20) comment '公司',
    city varchar(20) comment '城市',
    unique index uk_company_city(company, city)
);

-- 成功插入第一条数据
insert into test_unique_key values(1, '公司1', null);
-- 成功插入第二条数据
insert into test_unique_key values(2, '公司1', null);
-- 查询结果
select * from test_unique_key;

id|company|city|
--+-------+----+
 1|公司1    |    |
 2|公司1    |    |

2、原因

问题都已经复现出来了,那直接查看官方文档吧。对于唯一索引的相关介绍如下图所示:

image.png 原来官方文档已经说明了,对于唯一索引是允许NULL重复出现的。其实之前也知道唯一索引的字段是允许为NULL的,但潜意识里我只想到单字段唯一索引,从没考虑到它对联合唯一索引的影响。

在我们的一般认知里('公司1', null)与('公司1', null)是属于重复数据的。尤其对于从Oracle迁移到mysql的用户来说,更应该是这样的(Oracle是可以检测出冲突的)。而在mysql里,NULL是与任何值(包括NULL)都不相等的。

3、解决

两个思路:

  1. 数据库侧:在相关字段上添加not null约束(调整之前一定要和业务侧确认好)
  2. 业务侧:去掉对数据库的冲突异常的依赖,通过其他方式进行防重

具体选择哪种方式需要结合修改成本和后续的维护成本去权衡了。

而对于我们个人来说,后续在设计MySQL表的时候,尽量先在字段上加上not null约束。在MySQL上,因为NULL而引起的经典问题太多了,没必要太纠结,懂得都懂。