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高并发商城秒杀的更多相关文章

  1. JAVA构建高并发商城秒杀系统——架构分析

    面试场景 我们打算组织一个并发一万人的秒杀活动,1元秒杀100个二手元牙刷,你给我说说解决方案. 秒杀/抢购业务场景 商品秒杀.商品抢购.群红包.抢优惠劵.抽奖....... 秒杀/抢购业务特点 秒杀 ...

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

    转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含 ...

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

    1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(IN ...

  4. Alibaba高并发业务秒杀系统落地实战文档,已实践某大型秒杀场景

    前言: 高并发,几乎是每个程序员都想拥有的经验.原因很简单:随着流量变大,会遇到各种各样的技术问题,比如接口响应超时.CPU load升高.GC频繁.死锁.大数据量存储等等,这些问题能推动我们在技术深 ...

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

    转自:https://www.cnblogs.com/ajianku/p/10236573.html 第1章 课程介绍及项目框架搭建1-1 Java高并发商城秒杀优化导学1-2 项目环境搭建(Ecli ...

  6. PHP秒杀系统-高并发高性能的极致挑战

    慕课网实战教程后端:1.java c++算法与数据结构2.java Spring Boot带前后端 渐进式开发企业级博客系统3.java Spring Boot企业微信点餐系统4.java Sprin ...

  7. 使用Redis中间件解决商品秒杀活动中出现的超卖问题(使用Java多线程模拟高并发环境)

    一.引入Jedis依赖 可以新建Spring或Maven工程,在pom文件中引入Jedis依赖: <dependency> <groupId>redis.clients< ...

  8. PHP秒杀系统 高并发 高性能的极致挑战 下载

    第1章 课程介绍 秒杀系统在各种网站和应用中经常会用到.本课程从基本的系统设计和基础功能开始教导大家用PHP来设计和实现秒杀系统,并且为海量并发提供更高级的技术方案和实现手段. 第2章 系统技术选型分 ...

  9. PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法

    如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...

随机推荐

  1. netty系列之:选byte还是选message?这是一个问题

    目录 简介 类型的定义 搭建UDT stream服务器 搭建UDT message服务器 Stream和Message的handler 总结 简介 UDT给了你两种选择,byte stream或者me ...

  2. 2019年1月9日 ES6 学习心得

    ES6为我们创建对象提供了新的语法糖,这就是Class语法.如果你对ES5中面向对象的方式比较熟悉的话,Class掌握起来也是非常迅速的,因为除了写法的不同,它并不会增加新的难以理解的知识点.我们先利 ...

  3. 前端-Data URI Scheme

    了解Data URI scheme,首要要掌握一些URI.URL的基本知识,很多做移动端上开发的同学对这两个基本概念掌握的不够,本文首先会对这两个基本概念做一些简单的介绍. 基本概念 <HTTP ...

  4. Java线程--Phaser使用

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11867895.html Java线程--Phaser使用, 代码里头有详细注释: packag ...

  5. jqGrid 修改单元格值或者替换图片及其他

     var rowIds = jQuery("#list1").jqGrid('getDataIDs');                for (var k = 0; k < ...

  6. jenkins插件Publish Over SSH因安全问题下架

    最近用docker新搭建了一个jenkins,安装插件的时候发现publish over ssh找不到了,官方给出的解释是存在安全隐患于2022.01.12暂停分发,官方解释如下:https://ww ...

  7. 警惕!Python 中少为人知的 10 个安全陷阱!

    作者:Dennis Brinkrolf 译者:豌豆花下猫@Python猫 原题:10 Unknown Security Pitfalls for Python 英文:https://blog.sona ...

  8. iOS 屏幕录制实现

    iOS 屏幕录制实现 目录 iOS 屏幕录制实现 录屏API版本变化 App内部录制屏幕 录音麦克风声音 App内部录屏直播 Bonjour APP广播端实现 广播端App(直播平台)的实现 iOS1 ...

  9. Java在算法题中的输入问题

    Java在算法题中的输入问题 在写算法题的时候,经常因为数据的输入问题而导致卡壳,其中最常见的就是数据输入无法结束. 1.给定范围,确定输入几个数据 直接使用普通的Scanner输入数据范围,然后使用 ...

  10. vmstat监视内存的使用情况

    vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可实时动态监视操作系统的虚拟内存.进程.CPU活动. vmstat的语法 vmstat [-V] [-n] [ ...