社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

MySQL 更新不成功,事务问题搞清楚了吗?

码小辫 • 5 年前 • 637 次点击  

码小辫
专注更多编程视频和电子书
天天在用钱

作者:温安适
来源:https://my.oschina.net/floor/blog/4784609

问题

一个忙(mo)碌(yu)的下午,小航同学,突然大骂一声,“TM ,见鬼了,version没变,更新就是不成功”。

我看他,满头大汗,双手握拳,面目狰狞,似乎又要发作,赶紧说:“不成功没关系啊,重试就好,乐观锁一般是要重试的”

他略带鄙视的说道:代码有重试了逻辑,我还加日志了,结果发现version没变,就是更新不成功。

作为对技术小有追求的人,他怎么一说,立刻引起了我的好奇,随后诚恳的说道,我能看看代码吗?

小航,一句不发,双手却笔画了个请的姿势。

我仔细端详,代码大致逻辑如下:

@Transactional(timeout = 36000, rollbackFor = Throwable.class)
public void updateGoodNum(String id,Integer num)
      throws Exception 
{
    //1. select num as  dbnum,version as dbversion  from t where id=#{id}
    //2. update t set num=dbnum-num,version =dbversion +1  
    // where id=#{id} and version =dbversion  ;
    // 如果更新失败,重试1,2部总共3回
}

我轻叹了口气,在mysql连接工具执行了,如下语句,将截图发给小航后,

摆出个大师的模样说道:咱们测试环境隔离级别是RR(REPEATABLE-READ),在一个事务中重试是不行的!

小航难为情的说的:大哥,什么是隔离级别啊?为什么不行啊?怎么改呢?

隔离级别

隔离级别说明
READ UNCOMMITTED未提交读,会造成脏读,违反持久性D
READ COMMITTED读已提交数据, 会造成幻读 违反一致性C
REPEATABLE READ(RR)可重复读,默认隔离级别,事务中的select 语句会读取事务开始前的快照,当然也能读到本事务的更新内容
SERIALIZABLE不会使用mysql的mvcc机制,而是在每一个select请求下获得读锁,在每一个update操作下尝试获得写锁

update操作是读取当前值。

那在RR隔离级别下,为什么在一个事务中重试是不行的呢?

表格模拟,为什么不行?

开始事务前表t对应id=1的,version=1

事务Abegin事务Bbegin
1select version from t where id=1;-- 得到version=1update t set version=2 where id=1;commit;
2update t set XX where id=1 and version=1;// 更新失败,update读取当前,version=2
3select version from t where id=1;// 得到version=1
4commit

注意:事务中的select 是读快照,update是读当前。另外,MySQL 系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。

简单说,就去其他事务,已经将version修改了2,事务A看到的还是事务开始前的值,也就是version为1。

解决方式

RR隔离级别下,将重试移到事务外. 即每次重试重新开一个事务。

概要逻辑如下

/// 如果更新失败,重试updateGoodNum总共3回
public AFacadeImpl{
    @Autowired
    AService aservice;
    public void updateGoodsNum(){
        int i = 0
        while(!aservice.updateGoodNum(id,num) && i++ 3);
    }
}

public AServiceimpl implement AService{
    @Transactional(timeout = 36000, rollbackFor = Throwable.class)
    public boolean updateGoodNum(String id,Integer num)
          throws Exception 
{
        //1. select num as  dbnum,version as dbversion  from t where id=#{id}
        //2. update t set num=dbnum-num,version =dbversion +1  
        // where id=#{id} and version =dbversion  ;
        /

    }
}

-END-

关注视频号,参与留言送书活动

↓↓↓↓

一个认真分享的小编

前沿技术 /名企内推 /干货分享

商务合作:dot3721
长按左侧二维码添加

点分享

点点赞

点在看

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/117492