V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rqxiao
V2EX  ›  程序员

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

  •  1
     
  •   rqxiao · 2019-11-15 15:40:46 +08:00 · 5900 次点击
    这是一个创建于 1834 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    员工编号就是唯一的

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

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

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

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

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

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


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

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

    问题确实都有解决的方法,你说的数据转换层也许可以解决某些问题,但为此增加的成本和风险仍然是“弊端”,仍然需要考量如果只为了这么个事情引入一层架构是否合算。而且如果问题在于整个系统的假设(比如工号唯一)蔓延到多个模块以后被打破,可不是什么抽一层数据访问就能解决的
    leeg810312
        20
    leeg810312  
       2019-11-16 10:22:07 +08:00 via Android
    @mcfog 数据库系统的唯一性检查是在数据库系统里做的,你再怎么做并发检查,只要不在数据库系统里做,理论上还是有重复,你写的代码就是不可能避免并发冲突
    mtrec
        21
    mtrec  
       2019-11-16 10:25:02 +08:00 via Android
    mysql 里唯一约束的写入性能会差 因为要保证唯一性 不在内存的数据涉及随机 io 无法使用 change buffer
    arnoldFu
        22
    arnoldFu  
       2019-11-16 10:56:16 +08:00
    还是要在代码里做的吧,最经典的例子就是 name 不允许重复,但是允许用户做删除(系统逻辑删),这个时候数据库里 deleted 和 name 字段相同的数据就会有很多,这个时候 name 或者 name+deleted 作为唯一索引都不行,
    这种情况大家在开发中都是怎么解决的?
    wysnylc
        23
    wysnylc  
       2019-11-16 11:43:21 +08:00
    数据库唯一约束,分表无法使用
    要做严格唯一约束就弄个 redis 之类的内存校验或者从业务上允许重复
    单机环境爱咋搞咋搞,就那样
    Inside
        24
    Inside  
       2019-11-16 22:36:05 +08:00
    前面说利用数据库唯一索引的各位,应该考虑一下唯一索引为什么存在?我认为诸如唯一索引、外键等等约束,是因为在关系数据库诞生早期,是人工用 SQL 直接去操作数据库的,为了防止人为失误,所以建立了严格的约束。
    现在操作数据库的大都是自动化代码,这些事情代码都能干,而且表达能力更强。

    至于“再怎么做并发检查,只要不在数据库系统里做,理论上还是有重复”这样的观点,我只想说,难道关系数据库不是人写出来的?哪怕是不用数据库,只用各大语言的标准库去实现,也能轻松做到唯一索引这样的玩意,做不到还是别自称会写代码了。

    如果现代的程序员们做不到前辈们能做到的事情,那前辈们会对我们非常失望以及感到悲哀吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1992 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 00:02 · PVG 08:02 · LAX 16:02 · JFK 19:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.