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

原文地址:https://www.cnblogs.com/houss/p/10621033.html

时间: 2024-10-04 03:47:38

PHP高并发商城秒杀的相关文章

关于高并发和秒杀系统,你知道的和不知道的一些事

这篇文章也算是对于课程 <PHP秒杀系统 高并发高性能的极致挑战> 的一个整理,视频之外的另外一种形式吧. 大家也许开发过高并发的系统或者秒杀程序,但肯定都有接触过,像电商平台的秒杀.抢购等活动,还有12306春运抢票. 互联网公司,做一些有奖活动,而且数量有限,奖品给力,如果是先到先得的策略,那就类似秒杀系统了. 这类系统最大的问题就是活动周期短,瞬间流量大(高并发),很少人可以成功下单,绝大多数人都是很遗憾.所以从运营体验上,没有成功下单的人,心里肯定是不好受的,如果这时候,因为技术.网络

如何解决高并发,秒杀问题

相信不少人会被这个问题困扰,分享大家一篇这样的文章,希望能够帮到你! 一.秒杀业务为什么难做?1)im系统,例如qq或者微博,每个人都读自己的数据(好友列表.群列表.个人信息):2)微博系统,每个人读你关注的人的数据,一个人读多个人的数据:3)秒杀系统,库存只有一份,所有人会在集中的时间读和写这些数据,多个人读一个数据. 例如:小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万. 又例如:12306抢票,票是有限的,库存一份,瞬时流量非常多,都读相同的库存.读写冲突,锁非

(转)电商网站50W-100W高并发,秒杀功能是怎么实现的?

电商网站50W-100W高并发,秒杀功能是怎么实现的? 在淘宝.天猫.京东等国内大型电商平台“造节”的带领下,国内各电商平台纷纷跟进,双十一.双十二.618等电商专属节日也吸引了大量的用户参与.节前生意惨淡.访客寥寥,节日当天流量增长却异常迅猛,这对于广大程序猿同学和运维人员来说,无疑是巨大的考验. 秒杀系统的流量虽然很高,但是实际有效流量比较小:利用系统的层次结构,在每个阶段提前校验,拦截无效流量,可以减少大量无效流量涌入数据库,从而保障业务系统的正常运行: 第一步:利用浏览器缓存和CDN加速

【总结】瞬时高并发(秒杀/活动)Redis方案(转)

转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(INCR, DECR, INCRBY)来把字符串当作原子计数器使用 使用APPEND命令在字符串后添加内容 列表(List) Redis列表是简单的字符串列表,按照插入顺序排序 你可以添加一个元素到列表的头部(左边:

微信高并发抢红包秒杀实战案例

前言 群里有小伙伴咨询微信红包的架构,对于我来说,显然是不知道的,但是写一个相对高并发的抢红包案例还是完全可以的. 架构设计 业务流程 老板发红包,此时缓存初始化红包个数,红包金额(单位分),并异步入库. 抢红包,判断缓存剩余红包金额,剩余金额大于零则抢到红包,否则手慢了,红包派完了 拆红包,根据 redPacketId 获取分布式锁,如果获取到锁,红包个数减一,如果剩余红包个数大于零抢红包成功.否则失败.成功则计算红包金额,缓存总红包金额减去抢到的红包金额,异步入库.异步到账. 数据库设计 红

Java秒杀系统方案优化 高性能高并发实战 视频教程

第1章 课程介绍及项目框架搭建 1-1 Java高并发商城秒杀优化导学 1-2 项目环境搭建(Eclipse) 1-3 项目环境搭建(IDEA) 1-4 集成mybatis 1-5 安装redis 1-6 集成redis上 1-7 集成redis中 1-8 集成redis下第2章 实现用户登录以及分布式session功能 2-1 两次md5 2-2 登录功能实现上 2-3 登录功能实现下 2-4 jsr303参数校验 2-5 异常处理 2-6 分布式session上 2-7 分布式session

Java优化高性能高并发+高并发程序设计

第1章 课程介绍及项目框架搭建1-1 Java高并发商城秒杀优化导学1-2 项目环境搭建(Eclipse)1-3 项目环境搭建(IDEA)1-4 集成mybatis1-5 安装redis1-6 集成redis上1-7 集成redis中1-8 集成redis下第2章 实现用户登录以及分布式session功能2-1 两次md52-2 登录功能实现上2-3 登录功能实现下2-4 jsr303参数校验2-5 异常处理2-6 分布式session上2-7 分布式session下第3章 秒杀功能开发及管理后

Java优化高性能高并发+高并发程序设计视频教程

转自:https://www.cnblogs.com/ajianku/p/10236573.html 第1章 课程介绍及项目框架搭建1-1 Java高并发商城秒杀优化导学1-2 项目环境搭建(Eclipse)1-3 项目环境搭建(IDEA)1-4 集成mybatis1-5 安装redis1-6 集成redis上1-7 集成redis中1-8 集成redis下第2章 实现用户登录以及分布式session功能2-1 两次md52-2 登录功能实现上2-3 登录功能实现下2-4 jsr303参数校验2

Java高并发秒杀API之业务分析与DAO层

课程介绍 高并发和秒杀都是当今的热门词汇,如何使用Java框架实现高并发秒杀API是该系列课程要研究的内容.秒杀系列课程分为四门,本门课程是第一门,主要对秒杀业务进行分析设计,以及DAO层的实现.课程中使用了流行的框架组合SpringMVC+Spring+MyBatis,还等什么,赶快来加入吧! 第1章 课程介绍 本章介绍秒杀系统的技术内容,以及系统演示.并介绍不同程度的学员可以学到什么内容. 第2章 梳理所有技术和搭建工程 本章首先介绍秒杀系统所用框架和技术点,然后介绍如何基于maven搭建项