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; }
--------------小马哥原创------------------