被 mysql 的事务 和锁 搞懵了 - V2EX
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
kikione
V2EX    MySQL

被 mysql 的事务 和锁 搞懵了

  •  
  •   kikione 2021-01-15 15:47:17 +08:00 4912 次点击
    这是一个创建于 1744 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写了一个接口。

    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 条回复    2021-02-05 16:15:46 +08:00
    xxxyh
        1
    xxxyh  
       2021-01-15 15:55:01 +08:00
    建议把接口代码放上来
    bbao
        2
    bbao  
       2021-01-15 15:57:26 +08:00   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  
       2021-01-15 15:58:13 +08:00
    你这个 update 只针对一条数据的话没必要加索吧。更新失败就失败。
    也可以先锁住这一条,select for update 。这样其他线程操作同一个 id 的时候会等待。
    cheng6563
        4
    cheng6563  
       2021-01-15 16:01:38 +08:00
    单条 SQL 开不开没区别的,开事务其实只是关掉自动提交。
    bruce0
        5
    bruce0  
       2021-01-15 16:10:19 +08:00
    kikione
        6
    kikione  
    OP
       2021-01-15 16:12:44 +08:00
    @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
       2021-01-15 16:15:53 +08:00
    @bruce0 感谢
    xxxyh
        8
    xxxyh  
       2021-01-15 16:18:17 +08:00
    @kikione 接口代码有问题,前面 select 的时候没加锁,后面 update 的时候结果错误是正常的,如果不想加锁的话,可以把 sql 写成 set product_price = product_price + num
    notejava
        9
    notejava  
       2021-01-15 16:19:29 +08:00
    事务 != 加锁
    事务只能保证事务内的语句要么全部执行成功,要么全部失败。
    加锁是同一时刻,只允许一个线程修改数据。
    kikione
        10
    kikione  
    OP
       2021-01-15 16:23:13 +08:00
    @xxxyh 感谢,确实是 select 没有锁的问题,我刚想到,我改一下,再跑跑看
    keepeye
        11
    keepeye  
       2021-01-15 16:24:01 +08:00
    据我所知:除了 SERIALIZABLE,其他级别可能发生脏读或幻读现象。SERIALIZABLE 级别在事务中有 select 的时候可能会造成 update 死锁
    keepeye
        12
    keepeye  
       2021-01-15 16:24:59 +08:00
    你要串行化,那就 select for update 吧,但要小心性能问题
    cmai
        13
    cmai  
       2021-01-15 16:39:21 +08:00
    RR 读在不加 S/X 锁的情况下是快照读,多个线程可能同时读到了同一版本的数据,然后做更新
    kikione
        14
    kikione  
    OP
       2021-01-15 16:56:40 +08:00
    @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
       2021-01-15 17:08:46 +08:00
    @keepeye 对的, 我就是有 select 语句,少写了 sql,我的锅,谢谢您
    kikione
        16
    kikione  
    OP
       2021-01-15 17:17:47 +08:00
    @cheng6563
    大哥,想请问您,单条 sql 的话,多线程更新字段,为什么不会出错
    ,几百条线程去更新,也不会出现数据错误。
    cheng6563
        17
    cheng6563  
       2021-01-15 17:35:05 +08:00   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 nbsp;
       2021-01-15 18:15:26 +08:00
    你的问题不在于数据库中怎么加锁,而在于你需要给你的整个业务方法加锁。
    love2020
        19
    love2020  
       2021-02-05 16:15:46 +08:00
    @zifangsky 分布式锁吗
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2760 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 07:50 PVG 15:50 LAX 00:50 JFK 03:50
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86