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

请教大家: 如何解决数据库并发操作导致的数据不一致问题?(mysql)

  •  
  •   palmers · 2016-04-07 09:59:55 +08:00 · 5336 次点击
    这是一个创建于 3163 天前的主题,其中的信息可能已经有所发展或是发生改变。

    情形如下:

    1. A 、 B 同时读取到信息 张三 20 男
    2. 这时 A 在页面修改 张三 25 男 保存
    3. 然后 B 修改信息 张三 20 女 保存

    如此, A 的信息被覆盖了

    谢谢大家啦!

    第 1 条附言  ·  2016-04-07 12:40:35 +08:00

    谢谢大家啦, 结合大家的答案我再考虑下。 非常感谢大家!!!!

    26 条回复    2016-04-07 14:27:15 +08:00
    iMouseWu
        1
    iMouseWu  
       2016-04-07 10:15:28 +08:00
    乐观锁可以解决吧
    amlun
        2
    amlun  
       2016-04-07 10:18:01 +08:00
    没有看出数据不一致。。
    如果是防止同时更改,可以对该条数据加锁啊
    shoaly
        3
    shoaly  
       2016-04-07 10:19:48 +08:00
    这个不是锁能够搞定的, 而是程序逻辑的问题.
    A 改动的是 年龄
    B 改动的是性别
    两个操作本身不冲突的, 现在冲突是因为 代码修改的是全部...

    解决办法
    只保存 要修改的那一个字段, 不要都修改.
    hp3325
        4
    hp3325  
       2016-04-07 10:20:12 +08:00
    你可以加个时间戳, A 保存时更新时间戳, B 保存时获取时间戳,对比是否更新过,如果更新过,提示用户。
    甚至可以用存储过程,让数据库自动保证数据一致。
    UnisandK
        5
    UnisandK  
       2016-04-07 10:21:17 +08:00
    3L+1
    qiyuey
        6
    qiyuey  
       2016-04-07 10:23:16 +08:00
    这确实是程序的问题
    Jaylee
        7
    Jaylee  
       2016-04-07 10:24:02 +08:00
    你知道锁吗?
    shiny
        8
    shiny  
       2016-04-07 10:25:36 +08:00
    SELECT FOR UPDATE
    shiny
        9
    shiny  
       2016-04-07 10:27:00 +08:00
    保存前检查数据是否为修改前的数据
    Infernalzero
        10
    Infernalzero  
       2016-04-07 10:39:44 +08:00
    LZ 的问题不在于加不加锁,关键是都用了全量更新
    所以为了避免这种情况都是只更新修改的字段,非空字段不更新
    不要拿查询出来的对象去更新,而是 new 一个新的对象仅把之前查询出来的对象的主键 set 进去
    yangdehua
        11
    yangdehua  
       2016-04-07 10:56:00 +08:00
    事务隔离级别设置为, serializable
    连读都加锁,你说好不好啊
    loading
        12
    loading  
       2016-04-07 11:03:05 +08:00 via Android
    锁解决不了。

    前台只提交更改的就行。

    如果可以,考虑下 websock 实时更新并提醒。
    amlun
        13
    amlun  
       2016-04-07 11:06:56 +08:00
    @shoaly
    感觉问题被你带偏了。。。
    其实题主都没有说真实场景,万一张三的信息确实如 B 的更改呢?
    amlun
        14
    amlun  
       2016-04-07 11:08:32 +08:00
    所以题主---表达清楚需求啊!
    gamexg
        15
    gamexg  
       2016-04-07 11:17:06 +08:00
    楼主的这个场景涉及前台,无法直接锁行,同意 @hp3325 的观点,使用乐观锁,增加时间戳,保存时比较,冲突了提示用户手工修正。

    其他的并发能使用事务+悲观锁就直接锁行,简单方便快捷。
    likuku
        16
    likuku  
       2016-04-07 11:24:02 +08:00
    性别如今也不是不能改的...要政治正确啊。参考 FaceBook
    Numbcoder
        17
    Numbcoder  
       2016-04-07 11:25:06 +08:00
    用 etag
    A , B 在读取信息时,保存信息的 etag ,
    然后在发送更新请求时带上 etag ,执行数据库更新操作前检查 etag 是否变化,如果 etag 一致,保存更新,否则更新失败
    jugelizi
        18
    jugelizi  
       2016-04-07 11:49:43 +08:00
    这还叫并发 。。
    难道修改不应该保存修改记录吗?
    redis 好像有原子操作更新数据前检测数据有没有变化过
    yxzblue
        19
    yxzblue  
       2016-04-07 11:57:01 +08:00
    难道不是 1L 说的乐观锁就解决了么 ...
    yanyuan2046
        20
    yanyuan2046  
       2016-04-07 12:12:30 +08:00
    SELECT FOR UPDATE +1
    yanyuan2046
        21
    yanyuan2046  
       2016-04-07 12:16:54 +08:00
    说具体一下:
    看需求,并发修改同一行数据,
    如果期望 B 操作无效,用乐观锁
    如果期望按时间顺序执行( B 也会执行),用 select for update
    WangYanjie
        22
    WangYanjie  
       2016-04-07 12:58:11 +08:00
    你真的希望 A , B 的操作都生效吗?感觉会是一场灾难
    msg7086
        23
    msg7086  
       2016-04-07 13:11:01 +08:00
    两种场景。
    1. 单字段更新。写入数据库的时候只写入脏数据,这样就不会撤销之前的更改。
    2. 完整对象更新。如 13 楼所说,确实有按照 B 的修改完整写入的需求。
    david2016
        24
    david2016  
       2016-04-07 13:48:31 +08:00
    如果是 innodb 存储引擎的表的话,在这种并发写的情况下:
    两种决定因素:
    1 )事物隔离级别是哪种
    2 ) update 更新数据时候 where 查询条件列是否有索引,有的话是主键 /唯一 /非唯一索引
    whahuzhihao
        25
    whahuzhihao  
       2016-04-07 14:04:16 +08:00
    select for update 可以吧,一个人在操作一条数据的时候,另外一个连读这条数据的权限都不给
    Lullaby
        26
    Lullaby  
       2016-04-07 14:27:15 +08:00
    3L 真是目光如炬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2956 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 14:52 · PVG 22:52 · LAX 06:52 · JFK 09:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.