V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
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
xianyukang
V2EX  ›  MySQL

数据库中 select for update 和 version 乐观锁各自的适用场景是啥?

  •  
  •   xianyukang · 1 天前 · 1054 次点击

    (0) 如题

    (1) 比如转账/扣款相关的业务,推荐用悲观锁还是乐观锁?

    (2) 脑补了如下的乐观锁实现,可以满足并发转账和取钱需求吗?

    (3) 如果能,那么和悲观锁 select for update 作对比哪个更好?

    # 数据库初始状态
    TRUNCATE account;
    INSERT INTO account (id, name, balance, version)
    VALUES (1, 'ichigo', 10, 1),
           (2, 'rukia', 10, 1);
    
    # 并发事务 1 转出全部余额
    START TRANSACTION;
    SELECT balance, version FROM account WHERE id = 1;
    SELECT version FROM account WHERE id = 2;
    UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1;
    UPDATE account SET balance = balance + 10, version = 2 WHERE id = 2 AND version = 1;
    COMMIT;
    
    # 并发事务 2 取出全部余额
    START TRANSACTION;
    SELECT balance, version FROM account WHERE id = 1;
    UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1;
    COMMIT;
    
    7 条回复    2025-01-06 09:30:42 +08:00
    yidinghe
        1
    yidinghe  
       1 天前
    select for update 只适用于一次操作一条记录的情况,千万不要用于一次更新一大堆记录。转账扣款相关大多数情况下是适合用 select for update 的,因为每次只操作一条记录。这个是我工作当中用过的,互联网电商平台。比如有几个操作来自不同的服务,同时更新一条记录,但各自更新不同的字段。这个时候用 version 其实没必要,因为业务上它们并无冲突,所以还不如老老实实排队拿锁,依次做各自的事,反正几个操作加起来也不到 0.1 秒。
    xianyukang
        2
    xianyukang  
    OP
       1 天前
    @yidinghe
    确实,网络上的资料也经常告诫人们,不要用 select for update 对一大片记录加锁,要走「 唯一索引 」对指定记录加「 行锁 」确保锁的范围很小。另外你说的场景中,为啥更推荐排队拿锁啊? 比如做全量更新时用 version 不是能确保查出来的数据没有过期吗?
    enchilada2020
        3
    enchilada2020  
       1 天前
    避免使用 select for update ,MySQL 在这块有大坑,07 年的 bug 到现在还没修,报错信息压根驴唇不对马嘴,害我排查好几周:
    https://stackoverflow.com/questions/44949940/solution-for-insert-intention-locks-in-mysql
    ChoateYao
        4
    ChoateYao  
       1 天前
    一个是高并发解决方案,一个是数据过期解决方案。

    比如用户 A 、B 在同一时间读取记录 A ,用户 A 在 3 秒后提交数据;用户 B 在 10 秒后提交数据,这时候用户 B 提交的数据是过期数据,则不能让他提交成功。
    yidinghe
        5
    yidinghe  
       1 天前
    @xianyukang
    1 、select for update 在多线程环境下就是排队拿锁。第一个线程 commit 之后释放锁,下一个 select for update 的线程拿到锁。
    2 、全量更新不懂是什么意思,是指 a )更新一条记录的多个字段,还是 b )更新多条记录的一个字段。
    luciankaltz
        6
    luciankaltz  
       1 天前
    悲观锁,也就是 select for update ,一般同时满足两种情况
    1. 有一个大的事务(也就是不仅操作这一条记录,会涉及起码两条以上的数据库记录),并且需要保持这些操作都原子,或者全成功或者全失败
    2. 在整个事务周期中这个记录不能被其他请求修改
    理论上来说涉及到钱的一律全部悲观锁(基本上也满足以上两个场景)

    乐观锁的目的更多在于保持单条记录的原子性,比如操作记录对应的版本号,允许重试,但是版本更替的记录不能丢失。例如失败了可以通过重拾消息来保证最终更新成功,之类的
    cheng6563
        7
    cheng6563  
       1 天前
    就是这个名字的字面意思
    乐观锁:我觉得基本不会出现并发问题,但还是搞一个版本号来兜底,大不了报错让用户重试。
    悲观锁:我觉得很有可能会出现并发问题,这个业务还是执行前锁上相关资源比较好。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5552 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 06:50 · PVG 14:50 · LAX 22:50 · JFK 01:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.