MYSQL悲观锁-用户余额

无论什么锁JAVA的synchronized也好,还是MYSQL的锁都好,要注意分布式环境与单机环境

1.乐观锁

乐观认为并发不高,甚至没有并发。其中一种实现方式依靠在表中多加一个版本号字段,每次查询更新就按这个版本号,假设在修改时版本号与数据库不一致,就需要尝试重连(重新执行)。

查询

SELECT * FROM user WHERE id = #{id}

修改

UPDATE user SET .... WHERE id = #{id} AND version = #{version}

缺点:当出现并发时,可能会一直尝试重连

2.悲观锁

悲观认为并发经常出现,比如用户的余额问题。需要注意在Spring-Boot中悲观锁需要再事务逻辑中才会生效。悲观锁分为两种

悲观锁锁类 = 查询字段为索引 ? 行级锁 : 表级锁

实现方式

Mapper(核心为末尾的FOR UPDATE关键词)

@Select("SELECT * FROM person WHERE id = #{0} FOR UPDATE")
Person selectMoneyById(Integer id);

Service(核心为开头的事务)

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deductTicket(Integer aud, Integer ticket) 
        log.info("线程{}:start", Thread.currentThread().getName());
        Person person = baseMapper.selectMoneyById(aud);
        
        // 上面会阻塞,直到解锁(事务结束)
        log.info("线程{}:mid => {}", Thread.currentThread().getName(), person)
        
        if (person.getTicket() < ticket) {
            log.info("余额不足 => aud:{},targetTicket:{},nowTicket:{}", aud, ticket, person.getTicket());
            log.info("线程{}:end", Thread.currentThread().getName());
            return false;
        }
        
        // 加个延时看效果
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        baseMapper.updateById(person.setTicket(person.getTicket() - ticket));
        log.info("扣款成功 => aud:{},targetTicket:{},nowTicket:{}", aud, ticket, person.getTicket());
        log.info("线程{}:end", Thread.currentThread().getName());
        return true;
    }

--------------小马哥原创------------------

posted @ 2023-02-20 11:44  甜8筒  阅读(32)  评论(0编辑  收藏  举报