并发安全问题之超卖问题
乐观锁总结:
优点:不加锁性能好。
缺点:同时请求成功率低(即只要发现数据变了就放弃了)。
乐观锁思想的具体体现:一共两步,第一步,先查询状态。第二步,再更新数据时必须where等于前面的状态,确保数据没有改变。
第二步集查询和操作数据于一身具有原子性。
在有数量限制的业务中(即先查询的就是这个数据),要想提高成功率,可以在第二步中数据不必等于第一步查询的数据,而是小于第一步中查询的数据即可,这样就可以完美降低失败率,只有数量耗尽时才会失败。
**举个例子:**比如卖书,书有数量限制num_total=10,以卖出数量为num:
第一步查询:select * from book where id = xxx;
if (book.num < book.num_total) {可以继续执行第二步} else {拒绝卖书操作}
第二步查询加更新:update book set num = num + 1 where num < num_total;
第二步中没有使用标准的乐观锁即数据没有被操作才更新 (num = #{num})
而是让num < num_total只要没有达到库存上限即可操作,提高了并发的成功率,只要不是num=num_total都可以成功。
典型查询、判断、更新三个操作非原子性
解决方案-加锁即可,加锁的思想分为两类:悲观锁和乐观锁。
悲观锁是在操作数据之前先获取锁,有锁才能操作,这样安全了,但是性能下降了,把并发改为了单线程串行执行。
乐观锁不加锁,只会先查询一次,然后在操作数据时再查询一次(这里的查询与修改是原子性的,例如前面第一次查询变量a为1,等到操作数据时就要在SQL中update xxx set xxx=xxx where a = 1,这样就让查询和修改操作具有原子性),如果数据改变了就异常或者重试。