【一】.无锁
场景:多个人购买一件商品,库存减少。
商品表:
主键 商品名 库存数量 id name stock 1 美女 200
代码:
$table = 'think_goods'; $where = ['id' => 1]; $good = Db::table($table)->where($where)->find(); if (!$good) { throw new Exception('商品不存在'); } if ($good['stock'] <= 0) { throw new Exception('库存不足'); } $isUpDate = Db::table($table)->where($where)->dec('stock')->update(); if (!$isUpDate) { throw new Exception('扣除库存失败'); } echo '扣除库存成功,下单成功';
解释:这种代码虽然在查询库存的时候判断了库存数量大于0,但是并发下会出现库存为负值
【二】.乐观锁.
概念:乐观锁假设认为数据一般情况下不会造成冲突,只有数据在进行提交更新的时候才会检查会不会冲突,如果冲突返回错误信息给用户,不冲突则更新。乐观锁就是在应用层想办法解决。
场景:多个人购买一件商品,库存减少。
商品表:
主键 商品名 库存数量 id name stock 1 美女 10
代码1:
$table = 'think_goods'; $where = [ ['id', '=', 1], ['stock', '>', 0], ]; $isUpdate = Db::table($table)->where($where)->dec('stock')->update(); if(!$isUpdate) { throw new Exception('抢购失败,库存不足'); }
解释1:上面代码乐观的认为库存数量足够,因此只在最后更新库存的时候加上where条件,不满足条件无法更新,也是一种锁,更新失败提示用户库存不足即可。网上也有其他类似的方式,例如在表中添加一个version字段,每次更新的时候where条件加上version=查询的version,更新数据时再把version+1,其实道理一样。
【三】.悲观锁.
概念:在整个数据的处理过程中,将数据设置为锁定状态,因此悲观锁往往依赖于数据库提供的锁的机制。共享锁和排他锁都属于悲观锁
场景:多个人购买一件商品,库存减少。
商品表:
主键 商品名 库存数量 id name stock 1 美女 200
代码1: (排他锁,for update)
//01.开启事务 Db::startTrans(); try{ //02.加锁查询商品(for update 排他锁) $table = 'think_goods'; $where = ['id' => 1]; $info = Db::table($table)->where($where)->lock(true)->find(); if (!$info) { throw new Exception('商品不存在'); } //03.检查库存是否足够 if (!$info['stock']) { throw new Exception('库存不足'); } //04.更新商品库存 $isUpdate = Db::table($table)->where($where)->dec('stock')->update(); if(!$isUpdate) { throw new Exception('更新数据异常'); } //05.提交事务 Db::commit(); }catch (Exception $exception) { //06.事务回滚 Db::rollback(); //07.输出错误 var_dump($exception->getMessage()); } exit('抢购成功');
解释: 上面的代码使用了mysql的for update行锁,查询数据的时候就把本条数据锁定,其他进程只能读取不可修改本条记录,只到锁定的进程提交了事务或者回滚了事务,其他进程才能正常操作。上面的代码为了简洁外部少包装了异常try catch,遇到异常也回滚,否则容易造成死锁。但是如果是fpm这种情况就好多了,请求完成销毁一切,和mysql服务端断开连接,mysql发现客户端断开了则自动销毁锁。另外在测试mysql5.7中发现你长期通用锁不提交事务也不回滚,超过指定时间则自动销毁锁,抛出的异常(General error: 1205 Lock wait timeout exceeded; try restarting transaction)
提示:本文只是为了让大家区别乐观锁和悲观锁。而重点的悲观锁中的排他锁和共享锁将在其他文章叙述。一般来将大部分业务最好使用乐观锁处理,减少死锁或者表锁行锁的性能开销和安全风险。