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

update 大家会允许这样写吗?

  •  
  •   Niner · 16 小时 22 分钟前 · 3811 次点击
    User u = userMapper.selectOne(id);
    u.setName(myname);
    u.setAge(myAge);
    userMapper.updateById(u);
    

    我看工作里面很多代码都是这样更新的,虽然方便但是有并发问题,大佬们 cr 遇到会打回去吗?

    58 条回复    2025-01-23 14:23:55 +08:00
    kanepan19
        1
    kanepan19  
       16 小时 16 分钟前
    看场景,订单这样去更新要被批。
    用户这样更无所谓。
    ncbdwss
        2
    ncbdwss  
       16 小时 6 分钟前
    绝对不允许。
    KongLiu
        3
    KongLiu  
       16 小时 6 分钟前
    并发问题,建议加上乐观锁
    SingeeKing
        4
    SingeeKing  
       16 小时 5 分钟前 via iPhone
    不懂 java ,但为啥这样会有并发问题?按我的理解 u 是 select 返回的,updateById 的过程才会创建 transaction 并 commit ,理论上 u 不共享不会出并发问题?
    MooRider
        5
    MooRider  
       15 小时 27 分钟前
    @SingeeKing #4 第一个请求还没完成的时候第二个请求进来了, 由于一些原因导致第二个请求比第一个快, 最后的结果就变成了保存第一个请求的值
    sduoduo233
        6
    sduoduo233  
       15 小时 26 分钟前 via Android
    @SingeeKing 我也不懂 java ,但是我觉得如果在 select 和 update 之间 user 的某一项数据更新了,会被 update 覆盖
    zhouhu
        7
    zhouhu  
       15 小时 5 分钟前
    @kanepan19 大佬
    pelloz
        8
    pelloz  
       14 小时 59 分钟前   ❤️ 3
    看场景,没要求的位置这么写没问题,写完赶紧下班。有要求的位置简单加一个事务,赶紧下班。对性能有影响了?那就改为乐观锁,改完下班。
    crysislinux
        9
    crysislinux  
       14 小时 56 分钟前 via Android
    所以你们的每个表都有类似 version 字段的东西么,实践中很少这样吧,我看到的绝大部分更新都是这样更的。
    gadfly3173
        10
    gadfly3173  
       14 小时 53 分钟前
    理论上最好每次只更新自己的字段,但是那样写真的很麻烦吧。要通用的话你就得改造你的 DO ,让他能检测自己被改变了什么字段。
    csys
        11
    csys  
       14 小时 51 分钟前   ❤️ 1
    真要说起来,这才是“正确”的(或者说是现代的)写法,前提是有乐观锁或者分布式锁

    业务系统用数据库事务来做并发控制是个很不好的做法

    参考 DDD 中的 repository 和 unit of work 模式

    https://martinfowler.com/eaaCatalog/repository.html
    https://martinfowler.com/eaaCatalog/unitOfWork.html
    user8341
        12
    user8341  
       14 小时 20 分钟前
    用户资料(姓名、年龄)不就是他自己一个人会更改吗?应该不会有并发问题。
    dcsuibian
        13
    dcsuibian  
       13 小时 39 分钟前 via Android
    并发你这后端改了也没啥用

    实际上最有可能出现的并发问题不在这两行之间。而是前端的问题
    比如
    1 、甲打开了网页,拿到了 a 和 b 和两个字段,然后就去忙别的去了
    2 、乙也打开了网页,拿到了 a 和 b ,然后把 b 修改成 b1 ,接着把 a 和 b1 一起提交上去
    3 、甲回来了,把 a 修改成了 a1 ,然后把 a1 和 b 一起提交上去了
    4 、最终就输 a1 和 b ,b1 的修改丢失了

    正常前端都不会只传变更了的字段,而是偷懒一股脑都传上来
    所以你后端其实根本判断不了

    不过这其实也没啥大问题,因为通常来说一个表单的正确性就是取决于最后修改他的人而已
    既然最后修改他的人觉得没问题,那其实也确实没啥大问题
    xuanbg
        14
    xuanbg  
       8 小时 56 分钟前
    @SingeeKing 会出现你更你的我更我的,大家的原始数据都一样,先更的被后更的还原。
    chendy
        15
    chendy  
       6 小时 49 分钟前
    首先,一般没有 CR ,提了测了上了完事
    然后,除非并发问题会造成生命财产损失或者大量用户投诉,否则无需在意,更何况是一些并发极少的场景
    isnullstring
        16
    isnullstring  
       6 小时 30 分钟前
    不懂 JAVA
    要么先开事务,要么条件里带上原来的值,保证绝对更新
    Foxkeh
        17
    Foxkeh  
       6 小时 11 分钟前
    select for update 行锁
    Ayanokouji
        18
    Ayanokouji  
       6 小时 6 分钟前
    楼主是想问,patch or update 吧,java 一般都是全字段更新(过滤 0 值)。
    最好是 new 对象,set 对应值,而不是 select by Id ,然后 set 。
    不过,无并发冲突,两种都没问题。
    但是如果有并发,两种都有问题,都得加锁。
    gbw1992
        19
    gbw1992  
       5 小时 48 分钟前
    我想了一下,这样的问题除了给数据加上版本号貌似没什么特别好的处理办法了
    我觉得还是得看业务,如果这个数据多人共享且并发两很高,那必须加锁
    如果没这个需求应该无所谓
    angryfish
        20
    angryfish  
       5 小时 42 分钟前
    严格来说这样是有问题的。
    比如你这个场景 1 逻辑是修改了 name,age 。如果另外的场景 2 是修改用户的 phone,并且 phone 修改在前,那么这个 phone 的修改会被场景 1 的旧 phone 覆盖。
    oracleHe
        21
    oracleHe  
       5 小时 41 分钟前 via Android
    以面向对象的思维来说一般都是这么写的。因为往往在实际业务中你把对象 select 出来除了赋值可能还会做很多业务逻辑和计算,完了就将整个对象 update 。如果在要求比较高的场景下,要解决并发问题就通过分布式锁来处理。

    当然假如只是单纯更新数据的场景,这种写法并不好,因为可能只要更新一个字段却执行了两个 sql ,甚至把所有字段都更新了一遍,效率比较低,也存在你说的并发问题。所以简单的更新数据比较建议直接通过一句简单的 update 更新。
    oldking24
        22
    oldking24  
       5 小时 34 分钟前
    分场景呢,如果真的并发高那就加锁,要么乐观锁 version 字段,要么加分布式锁,按理说,用户不会天天修改自己的用户信息。
    xuanbg
        23
    xuanbg  
       5 小时 30 分钟前
    @dcsuibian 严格来说,不加锁的更新都有可能出你说的问题。加锁带来的问题是性能下降,和用户交互不友好。
    但这个问题可以通过业务规则规避,只允许一个用户修改数据就啥问题都没了。
    spritecn
        24
    spritecn  
       5 小时 29 分钟前
    没啥 大影响 ..很多代码这么写的,虽然新开一个 userForUpdate 类会清淅一点
    并不并发的,没区别,就算你只更新你的,并发你不加锁,开不开新类也控制不了
    对于阿里/腾讯 这类优化过的 mysql,会自动过滤不需要更新的字段,对性能影响也不大
    Vegetable
        25
    Vegetable  
       5 小时 27 分钟前
    所以,问题出在 mapper 的 updatebyid 并不会像符合认知的 orm 行为那样,只修改自己变更过的字段。而是会 update 整个对象除了 ID 外所有的字段。

    对不熟悉的人来说,这个还挺隐蔽的,但是想必已经是 java 生态内的常识了吧。
    gerefoxing
        26
    gerefoxing  
       5 小时 25 分钟前
    只更新需要更新的字段,全部更新没意义,重新 new 个对象更新
    cornorj6
        27
    cornorj6  
       5 小时 16 分钟前
    spring data jpa 中可以给实体加个 @Version 就能自动处理乐观缩,这种方式没有毛病,只有更新的一瞬间判断,感觉效率比行锁高。
    yor1g
        28
    yor1g  
       5 小时 15 分钟前
    那怎么写才正确呢 🤣 每个更新操作都考虑并发么
    chobitssp
        29
    chobitssp  
       5 小时 12 分钟前
    ef 使用 ChangeTracker 跟踪状态变化 只会更新变化的字段
    java 的 orm 不清楚是否支持
    wei2629
        30
    wei2629  
       5 小时 7 分钟前
    除非需要事务, 不然我觉得这就是最好的选择。
    cathub86
        31
    cathub86  
       4 小时 47 分钟前
    局部变量这样写 没事吧
    jnliyan1
        32
    jnliyan1  
       4 小时 44 分钟前
    查询是不是多余了
    cenbiq
        33
    cenbiq  
       4 小时 41 分钟前
    如果你用 C# ef orm ,官方就是推荐这么写的,但是 ef 自带乐观锁支持和 patch 更新,改了什么字段就更新什么字段
    carytseng
        34
    carytseng  
       4 小时 41 分钟前
    这是 mysql 经典的丢失更新问题,举个相似例子,a 用户读取 user1 时 age=0 并修改 age=age+1=1 ,b 用户读取 user1 时 age=0 并修改 age=age+2=2 ,此时丢失了 a 用户的修改,中间其实更新了+1 ,所以处理时如果要满足场景你在读前就要加锁
    YepTen
        35
    YepTen  
       4 小时 40 分钟前   ❤️ 1
    某些场景下,绝对允许;
    某些场景下,绝对不允许;
    某些场景下,无所屌谓;
    所以,你是哪个场景?
    wuhunyu
        36
    wuhunyu  
       4 小时 31 分钟前
    使用乐观锁更新的话,如果出现了并发有概率出现部分用户无法更新成功的情况,也就是数据库返回受影响行数为 0 ,这个时候需要提示用户修改失败,并要求重新刷新表单数据之后重新提交修改,并发量大的情况下,用户仍然有可能第二次提交也是失败的

    使用锁的话,大概率是可以更新成功的,但可能遇到的问题有,并发量大时可能会等待,表现为用户提交表单等待的时间会长一点或者直接等待超时(这种情况下,有可能修改成功,也可能失败)。此外还有一个问题是,后提交的用户有可能会把之前提前的信息给覆盖掉(比如 #13 提到的问题)

    如果按照锁升级策略来看的话,是否可以考虑如下策略
    默认使用乐观锁,当出现冲突时,也就是数据库返回受影响行数为 0 时,进入重锁模式,重锁模式释放完毕之后,再做一个判断,如果当前没有使用资源的线程,则切换回乐观锁模式
    Pursue9
        37
    Pursue9  
       4 小时 30 分钟前
    用 MP 的 LambdaUpdateWrapper ,只更新指定字段
    MoYi123
        38
    MoYi123  
       4 小时 28 分钟前
    没并发问题吧, 这里只是用了一个比较蠢的办法去构造了 User u 而已.
    pangdundun996
        39
    pangdundun996  
       4 小时 23 分钟前
    为啥不按需更新?
    ```java
    User u= new User();
    u.setId(id);
    u.setName(myname);
    u.setAge(myAge);
    userMapper.updateById(u);

    ```
    zzNaLOGIC
        40
    zzNaLOGIC  
       4 小时 6 分钟前
    就算是生成器生成的 mapper ,都有 updateByPrimaryKeySelective 吧
    用一下又不麻烦,这样就是纯懒到家了。
    chengyiqun
        41
    chengyiqun  
       3 小时 54 分钟前
    我知道 python 的 flask 在
    u.setName(myname);
    u.setAge(myAge);
    之后就可以直接 commit 了,
    java 好像真没啥好办法, 想要性能好还要加锁就只能乐观锁了.
    binge921
        42
    binge921  
       3 小时 53 分钟前
    我感觉 v 站最多的就是 java 选手了,哈哈哈哈
    angryfish
        43
    angryfish  
       3 小时 47 分钟前
    @binge921 #42 对于业务代码,除了 java 没哪个语言能打的。这也侧面反映,写业务逻辑的人占据了大部分的。
    chandlerbing9317
        44
    chandlerbing9317  
       3 小时 44 分钟前
    和语言没关系,对于先 check 后操作的场景就是典型的竞态条件,一般要加锁。但也分场景,比如上面说的订单场景,如果每条彼此有影响,比如先 check 是否为 0 ,更新的时候再原记录-1 肯定要加锁。如果用户资料修改,更改不依赖之前数据,那加不加都无所谓,纠结什么谁覆盖了谁的更新,谁先谁后没有任何意义。网络本身都可能有波动,不一定先请求的用户就一定先收到,也不代表一定先处理完。
    lyxxxh2
        45
    lyxxxh2  
       3 小时 41 分钟前
    我写 php 的,感觉还好。
    但是不需原数据做逻辑,查询就多余了。

    看到有说全部更新?
    应该不会吧,orm 会跟原数据对比,只更新改变的字段。

    字符串并发有什么影响??? 又不是金额之类的数字。
    就算是金额,你也要查询出来做校验啊!
    你这代码场景,我看不出有并发问题。
    maximdx
        46
    maximdx  
       3 小时 39 分钟前
    这个得看 orm 的实现还有配置?
    有的 orm 更新可以提供 TX 的
    lyxxxh2
        47
    lyxxxh2  
       3 小时 37 分钟前
    @Vegetable
    我擦,刚注意到你这个描述了。
    这已经是 bug 了!
    这么写,只能说脑*了。
    RandomJoke
        48
    RandomJoke  
       3 小时 15 分钟前
    user 场景来说,一般没什么问题,基本不会有什么并发。但这种全字段更新的,乐观锁控制下会减少点并发的问题。JPA 有自动乐观锁,不一定在这里体现。
    lingyumin1997
        49
    lingyumin1997  
       3 小时 11 分钟前
    这里并发会有数据安全的问题。
    如果不是业务场景迫不得已要先查询再根据查询的数据进行更新,不要这么写,直接 update 就好。
    有些业务情况下,有时必须要先查询再进行更新,这种情况建议加锁(乐观锁也好、其他方式加悲观锁也好)。
    NoKey
        50
    NoKey  
       3 小时 11 分钟前   ❤️ 1
    这个不能加 where 条件么?不满足条件不更新,就不会更新到已经被人更新过的数据了?
    Cyron
        51
    Cyron  
       3 小时 9 分钟前 via iPhone
    一楼已经给出答案
    horizon
        52
    horizon  
       3 小时 8 分钟前
    我不懂 java
    java 中没有原子操作吗
    bronyakaka
        53
    bronyakaka  
       2 小时 51 分钟前
    并发问题很大,必须上锁。
    1194129822
        54
    1194129822  
       1 小时 52 分钟前
    先读后写的情况下不都是事务了,没有并发问题。难带这种代码还没事务的吗。
    irisdev
        55
    irisdev  
       1 小时 42 分钟前
    哪有那么多并发问题..一般不会更新别人的数据吧,更新自己的数据客户端卡下,偶尔出问题运维下好了
    Configuration
        56
    Configuration  
       1 小时 30 分钟前
    1. 加锁
    2. 用 repeatable_read 事务

    但是不管哪种方式都或多或少影响性能
    bitmin
        57
    bitmin  
       1 小时 25 分钟前
    python 的 sqlalchemy 就超级无语,select 的时候就自动开启事务,还关闭不了
    iv8d
        58
    iv8d  
       46 分钟前
    根据你业务量决定,在一个就是关于金融积分等要避免,其他无所谓吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5103 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:10 · PVG 15:10 · LAX 23:10 · JFK 02:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.