【一】.无锁
场景:多个人购买一件商品,库存减少。
商品表:
主键 商品名 库存数量 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)
提示:本文只是为了让大家区别乐观锁和悲观锁。而重点的悲观锁中的排他锁和共享锁将在其他文章叙述。一般来将大部分业务最好使用乐观锁处理,减少死锁或者表锁行锁的性能开销和安全风险。
代码1:for循环批量插入100W数据<?php set_time_limit(0); $servername = "localhost"; $username = "root"; $password ...
通过经纬度转换为城市名称,并获得城市的编号,通常在全国类型的商城中比较通用。//经纬度转城市名称(返回城市id,城市名称) //$ak开发密钥,$lat纬度,$lng经度,$type返回数据类型 public function getCityName($ak,$lat,$lng)...
本篇文章不是讲解如何用.net开发自己的dll然后PHP通过com调用。主要记录PHP官方支持的DOTNET 基本语法如下:$obj = new DOTNET("assembly", "classname")a...
array_merge是最常用的数组合并方法,+号同样也可以,但是却有很大不同。array_merge遇到相同字符串key,后面数组的key会覆盖前面数组的key,+号正好相反。$a = [ 'one' => 'A on...
在编写多进程的实例中我在每个进程中使用如下代码://调用等待信号的处理器 while (true) { pcntl_signal_dispatch(); }开启5个进程,cpu直接100%修正之后的代码://调用等待信号的处理器 while&...
php7新增的特性(1).强制限制只能返回一种类型<?php class task { } //must return an integer function add(): int { &nb...