比如说普通的插入数据 员工表里有主键 员工编号
员工编号就是唯一的
这一步唯一性的校验 放在代码里判断 和 数据库里做约束 都有什么利弊吗
1
ZSeptember 2019-11-15 15:46:36 +08:00
这个不要考虑,肯定是数据库加唯一索引的。
代码做也是要加锁的。 |
2
reself 2019-11-15 15:49:08 +08:00 1
原子性
|
3
saulshao 2019-11-15 15:50:49 +08:00
在关系数据库出现之前,我估计世界上所有的数据都是用固定格式的文本 /二进制文件存储的。
那时都是在代码里判断唯一性。直到关系数据库出现...... |
4
taogen 2019-11-15 15:56:32 +08:00
放在代码里判断的情况
1. 并发。要自己实现,要控制并发和加锁。 2. 索引。唯一性的校验取出的数据没有索引,没有数据库效率高。 3. 性能。每次插入更新之前都要查一次数据库,好像也没有减少数据库的性能消耗。还不如让数据库系统自己校验。 |
5
mcfog 2019-11-15 15:56:54 +08:00 6
数据库做唯一的一个风险是跟不上业务,想改回代码里处理的时候有点儿蛋疼
就比如说你举的例子就很经典,员工工号似乎是唯一的,甚至能当主键,但比如明天的业务需求就可能变成:离职员工回归后新开账号(原有数据不继承)但工号仍使用原来的工号 如果你仍然坚持用数据库的唯一来处理工号,就得改全部有关联数据的地方增加重新入职的清理逻辑,可是很多数据甚至是不能清理的,一地鸡毛。如果是代码里的逻辑,直接再插一条数据完事儿,原来的唯一检查改成入职状态下工号唯一就行了 |
6
FaceBug 2019-11-15 15:58:56 +08:00
插入遇到重复时会造成自增不连续
|
8
stramkismet 2019-11-15 16:04:34 +08:00
数据库里加唯一索引比代码中加校验要更安全和简单一点。不需要考虑并发的问题。
在代码里校验的话千万别用“where 员工编号=员工编号” 来校验, 数据多了之后特别慢 |
10
mcfog 2019-11-15 16:41:43 +08:00 via Android
@tubimasky 离职 2 次呢,至少 MySQL 肯定没有 partial unique index 这么高级的
|
11
superrichman 2019-11-15 18:29:35 +08:00 via iPhone
@mcfog 同一公司离职两次还招进来了,是这个员工厉害还是公司牛 p ?
|
12
chendy 2019-11-15 18:42:14 +08:00
代码判断要做,有问题直接报错返回
数据库约束也要加,遇到并发 /业务漏洞可以兜底 员工编号不建议作主键,做个唯一约束就行 |
13
RedBeanIce 2019-11-15 20:17:31 +08:00
@superrichman 万一,现实就是这么残酷呢。。。。
|
16
Tneciv 2019-11-15 20:34:02 +08:00
@superrichman 实际上发生的概率很小 但是测试肯定会这么测
|
17
srx1982 2019-11-15 22:43:58 +08:00
如果 mysql 想分区,你那个唯一键的字段就要当主键
|
18
crclz 2019-11-16 07:58:10 +08:00
代码判断(×),代码判断+唯一索引(×),唯一索引(√)
我来解释一下。代码判断无法完全避免键冲突的并发。而不管有没有代码判断,数据库内部都会再做一次唯一性的判断。所以如果使用两者的话,会多一层性能损失。 可以使用捕获异常,或者更规范一点,postgres 的 insert ... on conflict(条件) do nothing returning id,再判断 id 是否为 null。mysql 的不知道怎么做。 @mcfog 做数据库模型变更,一地鸡毛,只能说明开发水平不够高。避免一地鸡毛的方法就是在领域层和基础设施层之间加一层转换层。或者传统的项目在 orm 里面改代码,让数据库迁移不过多“污染”业务代码。这层转换层一方面能保持原来的业务代码不需要更改,一方面在保存数据时如果发现是旧的数据模型,则附带做数据迁移。 |
19
mcfog 2019-11-16 08:23:41 +08:00 via Android
@crclz 如果你觉得写代码无法做到完全避免并发冲突,那数据库代码是怎么写出来的?
不同项目的需求又不一样,我不认为这个问题有唯一正确答案,我按照楼主的标题提出了一个弊端而已 问题确实都有解决的方法,你说的数据转换层也许可以解决某些问题,但为此增加的成本和风险仍然是“弊端”,仍然需要考量如果只为了这么个事情引入一层架构是否合算。而且如果问题在于整个系统的假设(比如工号唯一)蔓延到多个模块以后被打破,可不是什么抽一层数据访问就能解决的 |
20
leeg810312 2019-11-16 10:22:07 +08:00 via Android
@mcfog 数据库系统的唯一性检查是在数据库系统里做的,你再怎么做并发检查,只要不在数据库系统里做,理论上还是有重复,你写的代码就是不可能避免并发冲突
|
21
mtrec 2019-11-16 10:25:02 +08:00 via Android
mysql 里唯一约束的写入性能会差 因为要保证唯一性 不在内存的数据涉及随机 io 无法使用 change buffer
|
22
arnoldFu 2019-11-16 10:56:16 +08:00
还是要在代码里做的吧,最经典的例子就是 name 不允许重复,但是允许用户做删除(系统逻辑删),这个时候数据库里 deleted 和 name 字段相同的数据就会有很多,这个时候 name 或者 name+deleted 作为唯一索引都不行,
这种情况大家在开发中都是怎么解决的? |
23
wysnylc 2019-11-16 11:43:21 +08:00
数据库唯一约束,分表无法使用
要做严格唯一约束就弄个 redis 之类的内存校验或者从业务上允许重复 单机环境爱咋搞咋搞,就那样 |
24
Inside 2019-11-16 22:36:05 +08:00
前面说利用数据库唯一索引的各位,应该考虑一下唯一索引为什么存在?我认为诸如唯一索引、外键等等约束,是因为在关系数据库诞生早期,是人工用 SQL 直接去操作数据库的,为了防止人为失误,所以建立了严格的约束。
现在操作数据库的大都是自动化代码,这些事情代码都能干,而且表达能力更强。 至于“再怎么做并发检查,只要不在数据库系统里做,理论上还是有重复”这样的观点,我只想说,难道关系数据库不是人写出来的?哪怕是不用数据库,只用各大语言的标准库去实现,也能轻松做到唯一索引这样的玩意,做不到还是别自称会写代码了。 如果现代的程序员们做不到前辈们能做到的事情,那前辈们会对我们非常失望以及感到悲哀吧。 |