yongchao0 2019-07-01
乐观锁实际上是一种逻辑思想,并不是mysql 数据库的特性。这个要区分清楚。 实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
使用方式::
/** 伪代码 number 库存 goods_id 商品ID version 版本号默认为0 **/ $sql="select number from goods where goods_id={$goods_id} and version= {$version} "; // 时间戳方式查询库存 $sql="select number from goods where goods_id={$goods_id} and time < time() "; $rs=mysqli_query($conn,$sql); $row = $rs->fetch_assoc(); if($row['number']>0){//高并发下会导致超卖 if($row['number']<$number){ return insertLog('库存不够',3,$username); } //库存减少 $sql="update goods set number=number-{$number},version += 1 where goods_id={$goods_id} and number>0"; // 时间戳方式减少库存 $sql="update goods set number=number-{$number},time = time() where goods_id={$goods_id} and number>0"; $store_rs=mysqli_query($conn,$sql); if($store_rs){ //生成订单,返回操作成功 echo json_encode(array('code'=>0,'msg'=>'库存减少成功','data'=>$username)); }else{ echo json_encode(array('code'=>1,'msg'=>'库存减少失败','data'=>$username)); } }else{ echo json_encode(array('code'=>3,'msg'=>'库存减少失败','data'=>$username)); }
注意:使用乐观锁需要注意啊,将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false
悲观锁利用了MySQL innoDB 存储引擎的支持行锁的特性。一行一行操作数据.
使用方式:
// 使用悲观锁锁住当前行 $sql="select number from goods where goods_id={$goods_id} for update "; // 减少库存操作 $sql="update goods set number=number-{$number} where goods_id={$goods_id} and number>0";注意:innoDB 行锁是基于索引来执行的,where 条件后必须有索引,不然走的就是全表扫描。
优点与不足:
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数乐观并发控制相信事务之间的数据竞争(datarace)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。
建议: 在用户并发量不大的应用场景下,采用乐观锁的方式。在对数据一致性要求很高的情况下,可以牺牲一下性能,采用悲观锁。这个两种方式的前提是采用MySQL的解决方案。