给数据库字段添加唯一性的字段约束有什么弊端吗

2019-11-15 15:40:46 +08:00
 rqxiao

比如说普通的插入数据 员工表里有主键 员工编号

员工编号就是唯一的

这一步唯一性的校验 放在代码里判断 和 数据库里做约束 都有什么利弊吗

5913 次点击
所在节点    程序员
24 条回复
ZSeptember
2019-11-15 15:46:36 +08:00
这个不要考虑,肯定是数据库加唯一索引的。
代码做也是要加锁的。
reself
2019-11-15 15:49:08 +08:00
原子性
saulshao
2019-11-15 15:50:49 +08:00
在关系数据库出现之前,我估计世界上所有的数据都是用固定格式的文本 /二进制文件存储的。
那时都是在代码里判断唯一性。直到关系数据库出现......
taogen
2019-11-15 15:56:32 +08:00
放在代码里判断的情况

1. 并发。要自己实现,要控制并发和加锁。
2. 索引。唯一性的校验取出的数据没有索引,没有数据库效率高。
3. 性能。每次插入更新之前都要查一次数据库,好像也没有减少数据库的性能消耗。还不如让数据库系统自己校验。
mcfog
2019-11-15 15:56:54 +08:00
数据库做唯一的一个风险是跟不上业务,想改回代码里处理的时候有点儿蛋疼

就比如说你举的例子就很经典,员工工号似乎是唯一的,甚至能当主键,但比如明天的业务需求就可能变成:离职员工回归后新开账号(原有数据不继承)但工号仍使用原来的工号

如果你仍然坚持用数据库的唯一来处理工号,就得改全部有关联数据的地方增加重新入职的清理逻辑,可是很多数据甚至是不能清理的,一地鸡毛。如果是代码里的逻辑,直接再插一条数据完事儿,原来的唯一检查改成入职状态下工号唯一就行了
FaceBug
2019-11-15 15:58:56 +08:00
插入遇到重复时会造成自增不连续
levon
2019-11-15 16:01:23 +08:00
@taogen 让数据库自己校验,代码不用校验了吗
stramkismet
2019-11-15 16:04:34 +08:00
数据库里加唯一索引比代码中加校验要更安全和简单一点。不需要考虑并发的问题。
在代码里校验的话千万别用“where 员工编号=员工编号” 来校验, 数据多了之后特别慢
tubimasky
2019-11-15 16:14:40 +08:00
@mcfog #5 这种加个 禁用标识位 使用联合约束呢
mcfog
2019-11-15 16:41:43 +08:00
@tubimasky 离职 2 次呢,至少 MySQL 肯定没有 partial unique index 这么高级的
superrichman
2019-11-15 18:29:35 +08:00
@mcfog 同一公司离职两次还招进来了,是这个员工厉害还是公司牛 p ?
chendy
2019-11-15 18:42:14 +08:00
代码判断要做,有问题直接报错返回
数据库约束也要加,遇到并发 /业务漏洞可以兜底
员工编号不建议作主键,做个唯一约束就行
RedBeanIce
2019-11-15 20:17:31 +08:00
@superrichman 万一,现实就是这么残酷呢。。。。
optional
2019-11-15 20:20:54 +08:00
@mcfog 离职两次当然是改成 integer 的标志位
optional
2019-11-15 20:22:07 +08:00
@mcfog 😹忘记了
Tneciv
2019-11-15 20:34:02 +08:00
@superrichman 实际上发生的概率很小 但是测试肯定会这么测
srx1982
2019-11-15 22:43:58 +08:00
如果 mysql 想分区,你那个唯一键的字段就要当主键
crclz
2019-11-16 07:58:10 +08:00
代码判断(×),代码判断+唯一索引(×),唯一索引(√)

我来解释一下。代码判断无法完全避免键冲突的并发。而不管有没有代码判断,数据库内部都会再做一次唯一性的判断。所以如果使用两者的话,会多一层性能损失。
可以使用捕获异常,或者更规范一点,postgres 的 insert ... on conflict(条件) do nothing returning id,再判断 id 是否为 null。mysql 的不知道怎么做。


@mcfog 做数据库模型变更,一地鸡毛,只能说明开发水平不够高。避免一地鸡毛的方法就是在领域层和基础设施层之间加一层转换层。或者传统的项目在 orm 里面改代码,让数据库迁移不过多“污染”业务代码。这层转换层一方面能保持原来的业务代码不需要更改,一方面在保存数据时如果发现是旧的数据模型,则附带做数据迁移。
mcfog
2019-11-16 08:23:41 +08:00
@crclz 如果你觉得写代码无法做到完全避免并发冲突,那数据库代码是怎么写出来的?

不同项目的需求又不一样,我不认为这个问题有唯一正确答案,我按照楼主的标题提出了一个弊端而已

问题确实都有解决的方法,你说的数据转换层也许可以解决某些问题,但为此增加的成本和风险仍然是“弊端”,仍然需要考量如果只为了这么个事情引入一层架构是否合算。而且如果问题在于整个系统的假设(比如工号唯一)蔓延到多个模块以后被打破,可不是什么抽一层数据访问就能解决的
leeg810312
2019-11-16 10:22:07 +08:00
@mcfog 数据库系统的唯一性检查是在数据库系统里做的,你再怎么做并发检查,只要不在数据库系统里做,理论上还是有重复,你写的代码就是不可能避免并发冲突

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/619930

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX