PHP高并发商城秒杀
1.什么是秒杀
秒杀活动是一些购物平台推出的集中人气的活动,一般商品数量很少,价格很便宜,限定开始购买的时间,会在以秒为单位的时间内被购买一空。比如原价千元甚至万元的商品以一元的价格出售,但数量只有一件,在某天的某个时间开始出售,这就造成很多人去抢这一件商品。当然想抢到是需要很多因素的,比如你的电脑配置、网速,还有你的运气。
2.秒杀会带来的问题
(1)、高并发
比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验。
(2)、超卖
任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难题。
3.解决的方式
前台方面:
A:扩容
加机器,这是最简单的方法,通过增加前端池的整体承载量来抗峰值。
B:静态化
将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。
C:限流
一般都会采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。或者活动入口的时候增加游戏或者问题环节进行消峰操作。
后台方面:
A: 锁机制
乐观锁,就是在数据库设计一个版本号的字段,每次修改都使其+1,这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。
悲观锁,就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update,这样其他线程将无法提交数据。
文件锁,通过锁定文件来判断,确保其他线程无法提交数据
B: redis队列
引入队列,然后将所有写DB操作在单队列中排队,完全串行处理。当达到库存阀值的时候就不在消费队列,并关闭购买功能。这就解决了超卖问题。
优点:解决超卖问题,略微提升性能。
缺点:性能受限于队列处理机处理性能和DB的写入性能中最短的那个,另外多商品同时抢购的时候需要准备多条队列。
4.演示
原生写法,会出现多卖现象
<?php
header("content-type:text/html;charset=utf-8"); $dsn='mysql:host=localhost;dbname=test';
try {
$conn= new PDO($dsn, 'root', 'root');
$conn->exec("set names utf8"); } catch (PDOException $e) {
exit('数据库连接失败,错误信息:'. $e->getMessage());
} $price = 10;
$user_id = 1;
$goods_id = 1;
$sku_id = 11;
$number = 1; //生成唯一订单
function build_order_no(){
return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
} $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";
$rs = $conn->query($sql);
$row = $rs->fetch(); //获取一行数据 if ($row['number']>0) {//库存是否大于0
$order_sn=build_order_no();
$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
$conn->query($sql); //库存减少
$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
$f = $conn->query($sql);
if ($f) {
// 库存减少成功
} else {
// 库存减少失败
}
} else {
// 库存不够
}
echo "success";
通过文件锁来解决
<?php
header("content-type:text/html;charset=utf-8"); $dsn='mysql:host=localhost;dbname=test';
try {
$conn= new PDO($dsn, 'root', 'root');
$conn->exec("set names utf8"); } catch (PDOException $e) {
exit('数据库连接失败,错误信息:'. $e->getMessage());
} $price = 10;
$user_id = 1;
$goods_id = 1;
$sku_id = 11;
$number = 1; //生成唯一订单
function build_order_no(){
return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";//解锁 此时ih_store数据中goods_id='$goods_id' and sku_id='$sku_id' 的数据被锁住(注3),其它事务必须等待此次事务 提交后才能执行
$rs = $conn->query($sql);
$row = $rs->fetch(); //获取一行数据 $fp = fopen("lock.txt", "w+"); // 通过文件锁住操作执行完再执行下一个
if (!flock($fp, LOCK_EX | LOCK_NB)) {
echo "系统繁忙,请稍后再试";
return;
} if ($row['number']>0) {//库存是否大于0
$order_sn=build_order_no();
$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
$conn->query($sql); //库存减少
$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
$f = $conn->query($sql);
if ($f) {
// insertLog('库存减少成功');
flock($fp, LOCK_UN);//释放锁
} else {
// insertLog('库存减少失败');
}
} else {
// insertLog('库存不够');
}
fclose($fp);
echo "success";
通过redis队列来实现
客户端
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis_name = 'miaosha';
$sales='goods_store';
//
$uid = mt_rand(1000,9999);
$store = 10;
usleep(100000);// usleep()函数的功能是把调用该函数的线程挂起一段时间 [1] , 单位是微秒(microseconds:即百万分之一秒)
if ($store > $redis->get($sales)) {
$redis->incr($sales);
$redis->lpush($redis_name, $uid);//
// echo $uid."秒杀成功|";
} else {
// echo "秒杀已结束";
} $redis->close();
服务端
<?php
header("content-type:text/html;charset=utf-8"); $dsn='mysql:host=localhost;dbname=test';
try {
$conn= new PDO($dsn, 'root', 'root');
$conn->exec("set names utf8"); } catch (PDOException $e) {
exit('数据库连接失败,错误信息:'. $e->getMessage());
} $price = 10;
$user_id = 1;
$goods_id = 1;
$sku_id = 11;
$number = 1; //生成唯一订单
function build_order_no(){
return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
} $redis = new Redis();
$result = $redis->connect('127.0.0.1',6379);
//通过死循环从队列中一直取值
while (true) {
//模拟下单操作
//下单前判断redis队列库存量
$userid = $redis->rpop('miaosha'); if(!$userid){
// insertLog('error:no store redis');
continue;
}
//生成订单
$order_sn = build_order_no();
$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
values('$order_sn','$userid','$goods_id','$sku_id','$price')";
$conn->query($sql); //库存减少
$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
$f = $conn->query($sql);
if ($f) {
// insertLog('库存减少成功');
}else{
// insertLog('库存减少失败');
}
}
$redis->close();
压力测试工具jmeter安装方法 : https://www.cnblogs.com/houss/p/10619453.html
PHP高并发商城秒杀的更多相关文章
- JAVA构建高并发商城秒杀系统——架构分析
面试场景 我们打算组织一个并发一万人的秒杀活动,1元秒杀100个二手元牙刷,你给我说说解决方案. 秒杀/抢购业务场景 商品秒杀.商品抢购.群红包.抢优惠劵.抽奖....... 秒杀/抢购业务特点 秒杀 ...
- 【总结】瞬时高并发(秒杀/活动)Redis方案(转)
转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含 ...
- 【总结】瞬时高并发(秒杀/活动)Redis方案
1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(IN ...
- Alibaba高并发业务秒杀系统落地实战文档,已实践某大型秒杀场景
前言: 高并发,几乎是每个程序员都想拥有的经验.原因很简单:随着流量变大,会遇到各种各样的技术问题,比如接口响应超时.CPU load升高.GC频繁.死锁.大数据量存储等等,这些问题能推动我们在技术深 ...
- Java优化高性能高并发+高并发程序设计视频教程
转自:https://www.cnblogs.com/ajianku/p/10236573.html 第1章 课程介绍及项目框架搭建1-1 Java高并发商城秒杀优化导学1-2 项目环境搭建(Ecli ...
- PHP秒杀系统-高并发高性能的极致挑战
慕课网实战教程后端:1.java c++算法与数据结构2.java Spring Boot带前后端 渐进式开发企业级博客系统3.java Spring Boot企业微信点餐系统4.java Sprin ...
- 使用Redis中间件解决商品秒杀活动中出现的超卖问题(使用Java多线程模拟高并发环境)
一.引入Jedis依赖 可以新建Spring或Maven工程,在pom文件中引入Jedis依赖: <dependency> <groupId>redis.clients< ...
- PHP秒杀系统 高并发 高性能的极致挑战 下载
第1章 课程介绍 秒杀系统在各种网站和应用中经常会用到.本课程从基本的系统设计和基础功能开始教导大家用PHP来设计和实现秒杀系统,并且为海量并发提供更高级的技术方案和实现手段. 第2章 系统技术选型分 ...
- PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法
如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...
随机推荐
- 如何完整删除DISK DRILL
前两天装了DISK DRILL 右上角出现一个温度提示的图标 现在把DISK DRILL卸载了 但右上角的温度提示图标仍然存在 请问如何删除? 打开系统偏好设置----用户与群----管理员(点 ...
- cloudstack-4.1.5版本最全入门笔记【2022】
cloudstack简介 CloudStack是一个开源的具有高可用性及扩展性的云计算平台.目前Cloudstack支持管理大部分主流的hypervisors,如KVM,XenServer,VMwar ...
- 给 zsh 自定义命令添加参数自动补全
有时我会自定义一些 zsh 命令,以便提升某些高频操作的效率.本文记录我给一个自定义命令添加参数自动补全的方法. 场景 我自定义了一个 zsh 命令 gmt,执行 gmt <b2>,可以将 ...
- Postman_JavaScript
使用语法:JavaScript 结构: 测试工具主要包括三部分 在发起请求之前运行的Pre-request,预处理数据,作用:在发送请求前编辑请求数据,比如用户名或时间戳 对响应后的数据运行的Test ...
- jquery-easyui环境的搭建及测试
对于软件开发者来说,一个良好的前端框架不仅能够使页面优美可观而且还能够大大的提高开发效率.提高系统整体界面的美观,框架将常用的功能封装完成,减少工作量.前端框架目前也比较多,小编本次主要介绍下easy ...
- SQL server 查询当前数据库所有表的行数
SELECT OBJECT_NAME(ii.id) TableName ,rows FROM sysindexes ii INNER JOIN sysobjects oo ON ( oo.id = i ...
- [LeetCode]35.搜索插入位置(Java)
原题地址: search-insert-position 题目描述: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置. 请必须使 ...
- Realtime Data Processing at Facebook
概要 这篇论文发表于2016年,主要是介绍Facebook内部的流式计算平台的设计与思考,对于流式计算的关键特性的实现选型上进行深度对比分析. 流式计算系统5个衡量指标 文中提到有5个重要的考量部分 ...
- 轩辕展览-为什么要做VR虚拟展厅设计?
沉浸感,有趣和互动体验VR虚拟展厅设计给客户带来高度的沉浸感和互动体验,给客户一种真实的感觉,让客户更愿意参与,使商家的宣传更加客观. 展示方式多样化 ,增加宣传优势在展示产品或企业时,VR全景可达到 ...
- Win10 装好 Hyper-V 又要将其卸载方法记录
现需要在 windows 上安装虚拟机使用 linux 系统,遂想起来 win10 本身就有 Hyper-V 虚拟工具,但是 win10 家庭版并没有这个服务,百度之,找到了方法(Win10家庭版怎么 ...