› MySQL 5.5 Community Server
› MySQL 5.6 Community Server
› Percona Configuration Wizard
› XtraBackup 搭建主从复制
Great Sites on MySQL
› Percona
› MySQL Performance Blog
› Severalnines
推荐管理工具
› Sequel Pro
› phpMyAdmin
推荐书目
› MySQL Cookbook
MySQL 相关项目
› MariaDB
› Drizzle
参考文档
› http://mysql-python.sourceforge.net/MySQLdb.html
duanzs
V2EX  ›  MySQL

一个关于 mysql 锁问题

  •  
  •   duanzs · Jun 21, 2020 · 3942 views
    This topic created in 2292 days ago, the information mentioned may be changed or developed.

    背景:一个定时任务(多实例),取表 A 中状态为“初始化”的记录,然后取表 B 中对应记录,B 库存-A 消费存在两种情况。若 B-A=0,AB 记录都职位“完成”状态;若 B-A>0,AB 记录都职位“完成”状态后,再生成一条新的 B 记录,库存字段值=B-A ;

    正常情况一:AB 各一条记录,库存与消费相等,AB 完成; 正常情况二:AB 各一条记录,库存大于消费,AB 完成,新生成一条库存记录 B ; 正常情况三:库存 B 一条记录,值为 8,消费 A1 、A2 两条记录,值都为 4 。首先 A1 与 B 进行操作,8-4=4>0,A1 和 B 完成,然后生成一条新 B,值为 4,然后 A2 和新 B 操作后全部完成。

    异常情况:根据正常情况三,A1 与 A2 先后执行没问题,但是因为是多实例,A1 和 A2 可能同时执行,同时查询到 B 那一条记录,然后俩都 8-4=4>0,最后生成俩新 B 记录

    理想结果:B 那条记录能不能加个锁或其它手段控制一下(表数据量大,肯定不能锁整个表),让 A1 和 A2 先后顺序执行,避免同时执行

    注:业务比较复杂,并非只是库存-消费者一个小逻辑,此处简化问题了

    9 replies  •  2020-06-21 15:55:22 +08:00
    AngryPanda
        1
    AngryPanda  
       Jun 21, 2020
    内存锁,lockName = B 表的 ID
    AngryPanda
        2
    AngryPanda  
       Jun 21, 2020
    乐观锁: UPDATE/INSERT XXX WHERE B.quantity = 8
    AngryPanda
        3
    AngryPanda  
       Jun 21, 2020
    悲观锁:select xxx for update
    AngryPanda
        4
    AngryPanda  
       Jun 21, 2020
    其实我觉得设计上稍微有点奇怪了:B > A 库存表为啥需要新生成记录呢?
    duanzs
        5
    duanzs  
    OP
       Jun 21, 2020
    @AngryPanda 这只是一个简化描述,其实业务上远比描述的复杂
    peyppicp
        6
    peyppicp  
       Jun 21, 2020
    想顺序就 3 楼悲观锁,b 表里如果有唯一索引的话,select for update 行锁锁住,然后再做业务就行了,如果有并发也会在这行锁这里等待的。
    PopRain
        7
    PopRain  
       Jun 21, 2020   ❤️ 1
    update 库存表 set 库存=4 where PK=xx and 库存=8 肯定有一个 UPDATE 不成功的。。。。 (一般关键的更新,都要加上控制字段(或者说版本字段),例子中,原来的库存量就是“控制字段”,肯定要保证更新的是你原来的数据,这个都是开发 ERP 应用的常识。

    现在很多人都是根据主键去 UPDATE,当然有问题了(估计是某些 ORM 自身的限制,或者对 ORM 了解不深)
    crclz
        8
    crclz  
       Jun 21, 2020
    你这个是没有任何问题的。因为,你会将旧的 B 置为“完成”,相当于给 B 上了一个 X 锁,所以是没问题的。
    crclz
        9
    crclz  
       Jun 21, 2020
    同时,如果你的两个事务对 B 先读取的时候没有 FOR UPDATE,那么实际情况就会构成死锁,或者等待。当然,死锁是没有任何问题的,只不过会牺牲一个事务。整个流程是没有问题的,我只是解释一下内部运行过程。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   4649 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 07:39 · PVG 15:39 · LAX 00:39 · JFK 03:39
    ♥ Do have faith in what you're doing.