• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • dedecms
  • ecshop
  • z-blog
  • UcHome
  • UCenter
  • drupal
  • WordPress
  • 帝国cms
  • phpcms
  • 动易cms
  • phpwind
  • discuz
  • 科汛cms
  • 风讯cms
  • 建站教程
  • 运营技巧
您的位置:首页 > CMS教程 >建站教程 > 怎么解决PHP高并发(商品秒杀)问题?两种解决方案分享

怎么解决PHP高并发(商品秒杀)问题?两种解决方案分享

作者:站长图库 字体:[增加 减小] 来源:互联网

站长图库向大家介绍了PHP高并发,PHP商品秒杀等相关知识,希望对您有所帮助

怎么解决PHP高并发(商品秒杀)问题?下面本篇文章就来给大家分享两种解决方案(基于mysql或基于Redis),希望对大家有所帮助。


怎么解决PHP高并发(商品秒杀)问题?两种解决方案分享


秒杀会产生一个瞬间的高并发,使用数据库会增加数据库的访问压力,也会降低访问速度,所以我们应该使用缓存,来降低数据库的访问压力;

可以看出这里的操作和原来的下单是不一样的:产生的秒杀预订单不会马上写入数据库,会先写入缓存,等用户支付成功时,修改状态,写入数据库。

假设num是存储在数据库中的字段,保存了被秒杀产品的剩余数量。

if($num > 0){  //用户抢购成功,记录用户信息  $num--;}

假设在一个并发量较高的场景,数据库中num的值为1时,可能同时会有多个进程读取到num为1,程序判断符合条件,抢购成功,num减一。

这样会导致商品超发的情况,本来只有10件可以抢购的商品,可能会有超过10个人抢到,此时num在抢购完成之后为负值。

解决该问题的方案由很多,可以简单分为基于mysql和redis的解决方案,redis的性能要由于mysql,因此可以承载更高的并发量,不过下面介绍的方案都是基于单台mysql和redis的,更高的并发量需要分布式的解决方案,本文没有涉及。


一、基于mysql的解决方案

商品表 goods

CREATE TABLE `goods` ( `id` int(11) NOT NULL, `num` int(11) DEFAULT NULL, `version` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8

抢购结果表 log

CREATE TABLE `log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `good_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8


①悲观锁

悲观锁的方案采用的是排他读,也就是同时只能有一个进程读取到num的值。事务在提交或回滚之后,锁会释放,其他的进程才能读取。

该方案最简单易懂,在对性能要求不高时,可以直接采用该方案。要注意的是,SELECT … FOR UPDATE要尽可能的使用索引,以便锁定尽可能少的行数;

排他锁是在事务执行结束之后才释放的,不是读取完成之后就释放,因此使用的事务应该尽可能的早些提交或回滚,以便早些释放排它锁。

$this->mysqli->begin_transaction();$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1 FOR UPDATE");$row = $result->fetch_assoc();$num = intval($row['num']);if($num > 0){  usleep(100);  $this->mysqli->query("UPDATE goods SET num=num-1");  $affected_rows = $this->mysqli->affected_rows;  if($affected_rows == 1){    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");    $affected_rows = $this->mysqli->affected_rows;    if($affected_rows == 1){      $this->mysqli->commit();      echo "success:".$num;    }else{      $this->mysqli->rollback();      echo "fail1:".$num;    }  }else{    $this->mysqli->rollback();    echo "fail2:".$num;  }}else{  $this->mysqli->commit();  echo "fail3:".$num;}


②乐观锁

乐观锁的方案在读取数据是并没有加排他锁,而是通过一个每次更新都会自增的version字段来解决,多个进程读取到相同num,然后都能更新成功的问题。在每个进程读取num的同时,也读取version的值,并且在更新num的同时也更新version,并在更新时加上对version的等值判断。

假设有10个进程都读取到了num的值为1,version值为9,则这10个进程执行的更新语句都是

UPDATE goods SET num=num-1,version=version+1 WHERE version=9

然而当其中一个进程执行成功之后,数据库中version的值就会变为10,剩余的9个进程都不会执行成功,这样保证了商品不会超发,num的值不会小于0,但这也导致了一个问题,那就是发出抢购请求较早的用户可能抢不到,反而被后来的请求抢到了。

$result = $this->mysqli->query("SELECT num,version FROM goods WHERE id=1 LIMIT 1");$row = $result->fetch_assoc();$num = intval($row['num']);$version = intval($row['version']);if($num > 0){  usleep(100);  $this->mysqli->begin_transaction();  $this->mysqli->query("UPDATE goods SET num=num-1,version=version+1 WHERE version={$version}");  $affected_rows = $this->mysqli->affected_rows;  if($affected_rows == 1){    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");    $affected_rows = $this->mysqli->affected_rows;    if($affected_rows == 1){      $this->mysqli->commit();      echo "success:".$num;    }else{      $this->mysqli->rollback();      echo "fail1:".$num;    }  }else{    $this->mysqli->rollback();    echo "fail2:".$num;  }}else{  echo "fail3:".$num;}


③where条件(原子操作)

悲观锁的方案保证了数据库中num的值在同一时间只能被一个进程读取并处理,也就是并发的读取进程到这里要排队依次执行。

乐观锁的方案虽然num的值可以被多个进程同时读取到,但是更新操作中version的等值判断可以保证并发的更新操作在同一时间只能有一个更新成功。

还有一种更简单的方案,只在更新操作时加上num>0的条件限制即可。通过where条件限制的方案虽然看似和乐观锁方案类似,都能够防止超发问题的出现,但在num较大时的表现还是有很大区别的。

假如此时num为10,同时有5个进程读取到了num=10,对于乐观锁的方案由于version字段的等值判断,这5个进程只会有一个更新成功,这5个进程执行完成之后num为9;

对于where条件判断的方案,只要num>0都能够更新成功,这5个进程执行完成之后num为5。

$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1");$row = $result->fetch_assoc();$num = intval($row['num']);if($num > 0){  usleep(100);  $this->mysqli->begin_transaction();  $this->mysqli->query("UPDATE goods SET num=num-1 WHERE num>0");  $affected_rows = $this->mysqli->affected_rows;  if($affected_rows == 1){    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");    $affected_rows = $this->mysqli->affected_rows;    if($affected_rows == 1){      $this->mysqli->commit();      echo "success:".$num;    }else{    
  


 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 怎么解决PHP高并发(商品秒杀)问题?两种解决方案分享
  • PHP高并发实例详解之商品库存超卖并发测试
  • PHP高并发实例详解之解决商品库存超卖问题

相关文章

  • Uniapp中怎么使用scrpll-view组件实现下拉刷新
  • PS鼠绘清纯可爱的古装卡通小女孩
  • 如何利用CSS制作一个聚光灯效果(附代码)
  • JavaScript和CSS交互的5种方法的学习
  • 为kindsoft编辑器替换SyntaxHighlighter代码高亮,整合
  • PHP中fopen()函数的使用(附代码示例)
  • 说说Thinkphp5.1实现邮箱验证问题
  • Centos7下宝塔面板PHP7.3怎么安装sqlsrv扩展
  • php中如何获取当前的函数名
  • Photoshop制作彩色效果艺术字教程

文章分类

  • dedecms
  • ecshop
  • z-blog
  • UcHome
  • UCenter
  • drupal
  • WordPress
  • 帝国cms
  • phpcms
  • 动易cms
  • phpwind
  • discuz
  • 科汛cms
  • 风讯cms
  • 建站教程
  • 运营技巧

最近更新的内容

    • Photoshop制作绚丽的霓虹艺术字教程
    • 在centos下kanglephp怎么安装swoole扩展
    • PHP重定向如何实现数据不丢失?
    • Photoshop创建简洁绚丽的几何组合背景
    • 如何实现PHP中如果让字符串直接解析函数
    • 给DEDECMS后台加上批量修改tags功能
    • Photoshop图层样式制作质感光盘包装
    • 如何解决strict standards php报错问题
    • CDR快速制作质感立体字
    • DEDECMS调用指定栏目图片标签代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有