泰兴网站建设价格,盐城网站优化公司,wordpress跳出循环,最近的军事新闻大事10条一、首先来说一下什么是共享锁#xff1f;什么是排他锁#xff1f;
共享#xff1a;我可以读 写 加锁 , 别人可以 读 加锁。 排他#xff1a;只有我 才 可以 读 写 加锁 , 也就是说#xff0c;必须要等我提交事务#xff0c;其他的才可以操作。
二、简单例子实现加锁 锁… 一、首先来说一下什么是共享锁什么是排他锁
共享我可以读 写 加锁 , 别人可以 读 加锁。 排他只有我 才 可以 读 写 加锁 , 也就是说必须要等我提交事务其他的才可以操作。
二、简单例子实现加锁 锁和事务在使用时需要配合使用也就是用锁时需要先开启事务事务提交时会自动解锁。 DB::beginTransaction(); // 开启事务$good \App\Models\Good::sharedLock()-first(); //共享锁 s锁 读锁
// $good \App\Models\Good::lockForUpdate()-first(); //排他锁 x锁 写锁...DB::commit();
DB::beginTransaction();
$goodsInfo Goods::where(goods_id,$gid)-lockForUpdate()-first();
$goodsInfo-seckill_stock-1;
$goodsInfo-save();
DB::commit();三、怎样利用锁和事务解决并发问题
在我们的工作中常常会出现一些对数量控制有精确要求的需求比如商品库存量、奖品数量、报名人数限制等等这些应用场景往往都存在高并发可能比较容易出现数据量超量问题。以下做一下示例探索
1首先设计一个存量表
CREATE TABLE product (id int(11) NOT NULL AUTO_INCREMENT,product_name varchar(255) NOT NULL DEFAULT ,count int(10) NOT NULL DEFAULT 0,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4;
(2)添加一行数据如下设定基础库存量为 10 (3)问题代码如下 $process_num 50; //开50个进程模拟50个用户for ($i 0; $i $process_num; $i) {MultiProcessHelper::instance($process_num)-multiProcessTask(function () use ($i) {if (Db::name(product)-where(id, 1)-value(count) 0) {$res Db::name(product)-where(id, 1)-setDec(count);if ($res) {dump(获取到更新资源权限 . $i);}}});}
执行结果50 个用户都获取到了更新资源的权限但是数据库相应数据存量变成了 - 40 高并发带来的问题同一时刻有多个进程读取同一条数据同一时刻有多个进程更新同一条数据
4解决方案
1.方案1
要进行 DML 层面的限制最后关卡安全报错总比出现数据问题产生的影响小主要的修改是将 count 的类型改成了无符号整数这样该值就不可能再出现负数值
CREATE TABLE product (
id int(11) NOT NULL AUTO_INCREMENT,
product_name varchar(255) NOT NULL DEFAULT ,
count int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4;
执行一下代码当 count 值从 10 减到 0 时就不能再减少了再减就会出现数据库报错
2.方案2
mysql 提供的行级锁 select ... lock in share mode阻塞写select ... for update阻塞读写悲观锁所以 for update 机制能满足我们的原子要求。编辑代码如下 $process_num 50; //开50个进程模拟50个用户for ($i 0; $i $process_num; $i) {MultiProcessHelper::instance($process_num)-multiProcessTask(function () use ($i) {Db::startTrans(); //行级锁必须在事务中才能生效//设置for update进程会阻塞在这里只能允许一个进程获取到行锁其他等待获取if (Db::name(product)-where(id, 1)-lock(for update)-value(count) 0) { $res Db::name(product)-where(id, 1)-setDec(count);if ($res) {dump(获取到更新资源权限 . $i);}}Db::commit();});}
只有十个进程获取到了更新权限消费正常
3.方案3
将条件语句放到 update 上保持语句执行的原子性杜绝并发幻读 修改代码如下 $process_num 50; //开50个进程模拟50个用户for ($i 0; $i $process_num; $i) {MultiProcessHelper::instance($process_num)-multiProcessTask(function () use ($i) {//合并两条语句为一条更新语句$res Db::name(product)-where(id, 1)-where(count, , 0)-setDec(count);if ($res) {dump(获取到更新资源权限 . $i);}});}
只有十个进程获取到了更新权限消费正常
4.方案4
文件锁机制解决 $process_num 50; //开50个进程模拟50个用户for ($i 0; $i $process_num; $i) {MultiProcessHelper::instance($process_num)-multiProcessTask(function () use ($i) {$filename app()-getRootPath() . runtime/lock;$file fopen($filename, w); //打开文件$lock flock($file, LOCK_EX);// $lockflock($handle, LOCK_EX|LOCK_NB); 异步非阻塞所有进程如果出现获取不到锁不等待跳过加锁失败//获取文件排他锁:LOCK_EX异步阻塞只有一个进程获得锁其他竞争进程等待//还有一种共享锁:LOCK_SH所有进程都可以获取共享锁读取文件当且只有一个锁时才允许写操作否则操作失败容易出现死锁问题if ($lock) {try {if (Db::name(product)-where(id, 1)-lock(for update)-value(count) 0) {$res Db::name(product)-where(id, 1)-setDec(count);if ($res) {dump(获取到更新资源权限 . $i);}}} catch (\Exception $e) {dump($e-getMessage());} finally {flock($file, LOCK_UN); //无论如何都要释放锁}}fclose($file); //关闭文件句柄});}
只有十个进程获取到了更新权限消费正常
5.方案5
分布式锁机制解决
以上文件锁只适应于单体架构的需求在集群架构、分布式等多机联网结构中就是掩耳盗铃了所以适应性更好地锁机制还是要使用分布式锁分布式锁最常用和最易用就是 redis 的 setnx 锁了。 $process_num 50; //开50个进程模拟50个用户for ($i 0; $i $process_num; $i) {MultiProcessHelper::instance($process_num)-multiProcessTask(function () use ($i) {//获取redis锁//关于CacheHelper::getRedisLock是怎样获取锁的注意几个点就行1.如何避免死锁2.如何设置过期时间3.如何设置抢占条件4.如何循环等待判断。这些不在本文讨论范围可自行研究以后有空我也可以写一篇博文$lock CacheHelper::getRedisLock(redis_lock);if ($lock) {try {if (Db::name(product)-where(id, 1)-lock(for update)-value(count) 0) {$res Db::name(product)-where(id, 1)-setDec(count);if ($res) {dump(获取到更新资源权限 . $i);}}} catch (\Exception $e) {dump($e-getMessage());}} else {
// dump(获取redis锁失败);}});}
参考链接浅谈并发加锁 | Laravel China 社区 (learnku.com)