› 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
kikione
V2EX  ›  MySQL

被 mysql 的事务 和锁 搞懵了

  •  
  •   kikione · Jan 15, 2021 · 5574 views
    This topic created in 2084 days ago, the information mentioned may be changed or developed.

    写了一个接口。

    update order_a u set u.product_price=?2 where u.id=?1

    开启 50 个线程调接口修改 product_price,id 为主键。

    用乐观锁 数据正常。

    使用事务,当隔离级别为 SERIALIZABLE 时, 会成功部分,其他线程会报死锁。 使用事务,当隔离级别为 REPEATABLE_READ,READ_COMMITTED,READ_UNCOMMITTED,都会全部成功,只是数据不正确。

    使用事务不是会加锁吗? 为什么会数据不正常?大佬们求指点

    19 replies  •  2021-02-05 16:15:46 +08:00
    xxxyh
        1
    xxxyh  
       Jan 15, 2021
    建议把接口代码放上来
    bbao
        2
    bbao  
       Jan 15, 2021   ❤️ 1
    mysql 默认自动提交。
    使用事务,需要提前开始事务。

    start transaction;
    select xx from table where condition = ? for update ; (悲观锁)

    其他 session 查询相同 condition 时,会被锁住;直到超时或者对方 commit;

    乐观锁:
    select xx from table where condition = ? and u.id = ? and 待修改字段 = 原始值;

    如果待修改字段 != 原始值,表示已经有人修改了数据,你就直接业务返回就可以了。
    lllllliu
        3
    lllllliu  
       Jan 15, 2021
    你这个 update 只针对一条数据的话没必要加索吧。更新失败就失败。
    也可以先锁住这一条,select for update 。这样其他线程操作同一个 id 的时候会等待。
    cheng6563
        4
    cheng6563  
       Jan 15, 2021
    单条 SQL 开不开没区别的,开事务其实只是关掉自动提交。
    bruce0
        5
    bruce0  
       Jan 15, 2021
    kikione
        6
    kikione  
    OP
       Jan 15, 2021
    @xxxyh

    @Transactional(isolation = Isolation.REPEATABLE_READ)
    @Override
    public String changePrice(Integer num,String id) {
    OrderA one = orderAReposity.getOne("1");
    BigDecimal price = one.getProductPrice().add(BigDecimal.valueOf(num));
    int i = orderAReposity.changePrice1("1", price);
    return i+"";

    }
    接口代码
    kikione
        7
    kikione  
    OP
       Jan 15, 2021
    @bruce0 感谢
    xxxyh
        8
    xxxyh  
       Jan 15, 2021
    @kikione 接口代码有问题,前面 select 的时候没加锁,后面 update 的时候结果错误是正常的,如果不想加锁的话,可以把 sql 写成 set product_price = product_price + num
    notejava
        9
    notejava  
       Jan 15, 2021
    事务 != 加锁
    事务只能保证事务内的语句要么全部执行成功,要么全部失败。
    加锁是同一时刻,只允许一个线程修改数据。
    kikione
        10
    kikione  
    OP
       Jan 15, 2021
    @xxxyh 感谢,确实是 select 没有锁的问题,我刚想到,我改一下,再跑跑看
    keepeye
        11
    keepeye  
       Jan 15, 2021
    据我所知:除了 SERIALIZABLE,其他级别可能发生脏读或幻读现象。SERIALIZABLE 级别在事务中有 select 的时候可能会造成 update 死锁
    keepeye
        12
    keepeye  
       Jan 15, 2021
    你要串行化,那就 select for update 吧,但要小心性能问题
    cmai
        13
    cmai  
       Jan 15, 2021
    RR 读在不加 S/X 锁的情况下是快照读,多个线程可能同时读到了同一版本的数据,然后做更新
    kikione
        14
    kikione  
    OP
       Jan 15, 2021
    @cmai RR 读在不加 S/X 锁的情况下是快照读 这句话不是很懂,mysql 功力不足
    多个线程可能同时读到了同一版本的数据,然后做更新 这句话读懂了,谢谢。

    update order_a u set u.product_price=u.product_price+price where u.id=1 我改成这样的话,数据正常了
    kikione
        15
    kikione  
    OP
       Jan 15, 2021
    @keepeye 对的, 我就是有 select 语句,少写了 sql,我的锅,谢谢您
    kikione
        16
    kikione  
    OP
       Jan 15, 2021
    @cheng6563
    大哥,想请问您,单条 sql 的话,多线程更新字段,为什么不会出错
    ,几百条线程去更新,也不会出现数据错误。
    cheng6563
        17
    cheng6563  
       Jan 15, 2021   ❤️ 1
    @kikione
    常见几种情况我列下吧。

    1. 事务内单条 update
    update 有行锁,提交之后下一个 update 才能拿到锁继续操作,如果是 update xx set version=version+1 这样更新是没问题的。

    2.事务内先 select,然后根据 select 的结果再 update 。
    比如 select version from xx 把 version 放程序变量里,然后在程序里进行 version++,再 update xx set version=?。
    这种情况 select 是不加锁的,多个线程会一起拿到一个相同的 version,后续的 update 可能都是设置了相同的值。

    3.事务内先 select for update,然后根据 select 的结果再 update 。
    select 加了 for update 后也会加行锁,在你这个事务提交前其他线程的 select for update 也会卡住,直到事务提交后才能 select for update,数据也没问题了。
    zifangsky
        18
    zifangsky  
       Jan 15, 2021
    你的问题不在于数据库中怎么加锁,而在于你需要给你的整个业务方法加锁。
    love2020
        19
    love2020  
       Feb 5, 2021
    @zifangsky 分布式锁吗
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   813 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 21:55 · PVG 05:55 · LAX 14:55 · JFK 17:55
    ♥ Do have faith in what you're doing.