基于php的秒杀系统设计,php秒杀系统架构设计实例

6e777855a8d3a623db506fd4723375ea.png

① 对现有网站业务的冲击,如果秒杀程序部署到现有的服务器上,可能导致整个网站瘫痪

解决方法

把秒杀活动部署到单独的机子上,并且用单独的域名

② 高并发,用户在秒杀活动开始之前会不停的刷新页面,如果用php脚本连接数据库的方式,会对服务器的压力较大

解决方法

使用静态页面,并且使用cdn缓存,解决带宽压力大等问题

③ 避免用户直接通过下单连接下单

解决方法

带个随机参数,在秒杀开始之前才能得到

④ 控制抢购按钮,页面设计为静态页面并且使用了cdn缓存,如何点亮抢购按钮

解决方法

js文件后面带个随机版本号,这样不会被cdn缓存,直接到达服务器,来控制按钮点亮,这个js文件要小,不然会对服务器带来带宽的压力

⑤ 抢购程序设计,如果直接使用数据库事务,数据库压力太大

解决方法

使用redis或memcache等内存缓存,速度快还能解决超卖等问题

抢购静态页面代码

秒杀!

01天01时01分01秒

//动态加载js文件

document.write("");

使用二级域名,用cdn缓存html页面,css,js,图片等

nocdn.js生成脚本

$redis = new \Redis();

if ($redis->connect('127.0.0.1','6379') == false) {

die($redis->getLastError());

}

//设置token

$token=md5(rand(100,10000));

$redis->set("token",$token);

$hl=fopen("public/js/nocdn.js","w");

$js=<

'秒杀已经开始'+

'level1_button.jpg';

$(".jingshan").html(button);

$(function(){

var flag=1;

$("#qianggou").click(function(){

if(flag!=1){

return ;

}

flag=2;

var token='{$token}';

var url="http://192.168.128.128/redis.php"

$.ajax({

type: "POST",

url: url,

data: "token="+token,

success: function(msg){

alert( "Data Saved: " + msg );

}

});

})

})

EOF;

fwrite($hl, $js);

fclose($hl);

主要生成nocdn.js文件的内容,用linux crontab设置定时脚本

内容主要是显示秒杀的按钮,生成随机的参数,生成ajax的提交脚本,如果要跨域使用jsonp【推荐阅读: js跨域4种解决方案】

抢购代码

$redis = new \Redis();

if ($redis->connect('127.0.0.1','6379') == false) {

die($redis->getLastError());

}

//判断用户是否已经抢购

if($redis->hexists("mywatchlist","user_id_")==1){

exit("已经抢购");

}

//带参数的url

if($redis->get("token")!=$_GET['token']){

exit("参数错误");

}

$redis->watch("mywatchkey");//命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

$mywatchkey=$redis->get("mywatchkey");

$limit=10;

if($mywatchkey>=$limit){

exit("活动结束");

}

$redis->multi();//事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。

$redis->set("mywatchkey",$mywatchkey+1);

//sleep(5);//测试watch

$rob_result = $redis->exec();//按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil,在php中成功返回array(0->1)失败返回空

if($rob_result){

//保证库存,原子判断,确保当两个客户同时访问 Redis 服务器得到的是更新后的值

if($redis->incr("stock")>$limit){

echo "抢购失败,请重试";

}

echo "抢购成功";

//抢购成功

//$redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());

$redis->LPUSH("success",rand(1,20));

}else{

echo "抢购失败,请重试";

}

exit;

主要是redis的watch,如果执行事务发现mywatchkey变动过就执行事务失败,redis事务失败不会回滚,代码测试过

用ab测试没有超卖的问题

ab -n 1000 -c 1000 http://127.0.0.1/redis.php

上面的代码有个问题,就是会出现少卖的问题

下面来解释一下原因,

时间

客户端 A

客户端 B

T1

WATCH name

T2

MULTI

T3

SET name peter

T4

SET name john

T5

EXEC

在时间 T4 ,客户端 B 修改了 name 键的值, 当客户端 A 在 T5 执行 EXEC 时,Redis 会发现 name 这个被监视的键已经被修改, 因此客户端 A 的事务不会被执行,而是直接返回失败。

所以结果是name等于john

上面的代码sleep(5)可以测试,就会发现出现少买的问题,我测试去除sleep之后不会出现这个问题

上面就是我的秒杀设计

Demo:  http://pan.baidu.com/s/1bWa1cE

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值