NoSQL(Redis)秒殺

NoSQL(Redis)秒殺

概念

秒殺
說明:秒殺就是指商家的限時(shí)大甩賣(商家為了售賣商品所采取的一種銷售手段)
特征:1-限時(shí),2-低價(jià)
種類:一元秒殺、低價(jià)限量秒殺、低價(jià)限時(shí)限量秒殺
好處:因?yàn)槊霘a(chǎn)品參與者數(shù)量眾多,可以瞬間聚集人氣,提升品牌影響力,是一種不錯(cuò)的促銷手段。
并發(fā)
生活中:指同時(shí)有n個(gè)用戶一起去收營臺結(jié)賬的表現(xiàn)可以稱之為并發(fā)
網(wǎng)絡(luò)中:指同時(shí)有n個(gè)用戶一起訪問網(wǎng)站的表現(xiàn)可以稱之為并發(fā)
并發(fā)導(dǎo)致的問題:
生活中-忙不過來,程序中-服務(wù)器可能崩潰或者出現(xiàn)意外結(jié)果(負(fù)庫存)
    在計(jì)算機(jī)中:通過消息隊(duì)列實(shí)現(xiàn)

MySQL負(fù)庫存(秒殺可能出現(xiàn)的問題)

修改mysql.ini max_connections = 10 #聲明同時(shí)支持多少個(gè)用戶連接

打開 navicat.exe 執(zhí)行sql語句

create database if not exists miaosha charset=utf8;
use miaosha;
create table goods (id int primary key auto_increment,num int) engine=innodb;
insert into goods values (null, 100);

在站點(diǎn)目錄下創(chuàng)建mysql.php輸入下述命令

<?php
#語法:ab -n 1000 -c 100 請求地址
#說明:n-請求總數(shù), c- 每次請求量

//1.創(chuàng)建PDO對象
$pdo = new PDO('mysql:dbname=miaosha', 'root', 'root');

//2.查詢庫存
$pdoStatement = $pdo->query('select num from goods where id = 1');
$res = $pdoStatement->fetch(PDO::FETCH_ASSOC);
$num = $res['num'];

//3.判斷庫存
if ($num) {
    //減庫存
    $pdo->exec('update goods set num=num-1 where id = 1');
    echo '搶購成功';
} else {
    echo '對不起,你來晚了,庫存不足';
}

通過本地Apache安裝目錄下bin目錄中的ab測壓工具測試并發(fā)

ab.exe -n 1000 -c 100 http://127.0.0.1/mysql.php

多測試幾次 查看數(shù)據(jù)庫 可能會出現(xiàn)負(fù)庫存 這些是并發(fā)量高,數(shù)據(jù)處理不過來,當(dāng)前面用戶下單時(shí),后面用戶也讀取到了庫存數(shù)據(jù) 就會出現(xiàn)負(fù)庫存

Redis消息隊(duì)列(解決秒殺問題)

使用Workerman框架

下載Workerman框架....

在站點(diǎn)目錄下創(chuàng)建testworkerman.php輸入手冊中的定時(shí)器代碼

<?php  
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';

$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
    // 每2.5秒執(zhí)行一次
    $time_interval = 2.5;
    Timer::add($time_interval, function()
    {
        echo "task run\n";
    });
};

// 運(yùn)行worker
Worker::runAll();

打開DOS窗口通過php.exe執(zhí)行testworkerman.php文件查看效果

實(shí)現(xiàn)

登錄redis設(shè)置存放商品秒殺數(shù)據(jù)信息

    flushall
    hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3
    hmset goods_seckill_2 start_time 0 stop_time 0 price 30 real_num 2 seckill_num 2

在站點(diǎn)目錄下創(chuàng)建redis.php輸入下述命令

<?php
#步驟1:接受數(shù)據(jù)
$uid = 1;
$goods_id = 1;

#步驟2:連接Redis
$redis = new Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$redis->select(0);

#步驟3:過濾(判斷時(shí)間和庫存)
//獲取商品信息
$goodsInfo = $redis->hmget("goods_seckill_{$goods_id}"., array(
    'start_time', 'stop_time', 'real_num', 'seckill_num', 'price'
));
//判斷是否開始
//判斷是否結(jié)束
//判斷庫存
if ($goodsInfo['seckill_num'] < 1) {
    echo json_encode(array('state' => 0, '對不起,寶貝已被搶完!'));
    die;
}

#步驟4:將用戶請求加入消息隊(duì)列中
$len = $redis->lpush("goods_seckill_{$goods_id}_rs", $uid.'%'.$goods_id.'%'.$goodsInfo['price']);

#步驟5:判斷庫存(規(guī)則:隊(duì)列中前n個(gè)搶購成功)
if ($len > $goodsInfo['real_num']) {
//搶購失?。?duì)列長度 > 庫存)
    echo json_encode(array('state' => 0, '對不起,寶貝已被搶完!'));
    die;
} else {
//搶購成功,減庫存(注:千萬不能直接操作mysql因?yàn)橛胁l(fā)限制)
    echo json_encode(array('state' => 1, '秒殺成功'));
    die;
}

在站點(diǎn)目錄創(chuàng)建workerman.php輸入下述命令

<?php
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';

$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
    //每0.1秒執(zhí)行一次(精度可以達(dá)到毫秒0.001)
    $time_interval = 0.1; 
    Timer::add($time_interval, function()
    {
        $goods_id = 1;
        #步驟1:連接Redis
        $redis = new Redis;
        $redis->connect('192.168.159.128', 6379);
        $redis->auth('123');
        $redis->select(0);
        #步驟2:獲取秒殺相關(guān)信息
        $allowBuyNum = $redis->hget("goods_seckill_{$goods_id}", 'seckill_num'); //秒殺剩余庫存
        $orderInfoString = $redis->rpop("goods_seckill_{$goods_id}_rs");                     //隊(duì)列搶購用戶信息
        #步驟3:判斷(有庫存 && 有人搶購)
        if($allowBuyNum > 0 && $orderInfoString) 
        {
            echo "allowBuyNum:$allowBuyNum\n";

            #步驟4:減庫存
            $redis->hincrby('goods_seckill_1', 'seckill_num', -1);
            #步驟5:生成訂單
            $pdo = new \PDO('mysql:dbname=php15shop', 'root', 'root');

            $userOrderInfo = explode('%', $orderInfoString); //$uid.'%'.$goods_id.'%'.$price
                $order_id = date('Ymd').time().uniqid();
                $total_price = $userOrderInfo[2];
                $member_id = $userOrderInfo[0];
                $goods_id = $userOrderInfo[1];
                $create_time = time();
                $update_time = time();
                #主表(sh_order)
                $sql = "insert into sh_order (order_id, total_price, member_id, create_time, update_time)
                value 
                ('{$order_id}', $total_price, $member_id, $create_time, $update_time)";
                $pdo->exec($sql);
                #從表(sh_order_goods)
                $sql = "insert into sh_order_goods (order_id, goods_id, goods_number, goods_price) value('{$order_id}', $goods_id , $total_price, 2)";
                $pdo->exec($sql);
                
            echo "over...\n";
        }
    });
};

// 運(yùn)行worker
Worker::runAll();

通過DOS窗口運(yùn)行workerman.php文件,監(jiān)聽隊(duì)列數(shù)據(jù)

通過本地Apache安裝目錄下的bin目錄ab測壓工具測試并發(fā)

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

查看主表數(shù)據(jù)...

搭建秒殺項(xiàng)目虛擬主機(jī)

創(chuàng)建虛擬目錄seckill

將seckill項(xiàng)目解壓到站點(diǎn)目錄中

打開seckill站點(diǎn)目錄并修改數(shù)據(jù)庫信息

將之前的shop商城數(shù)據(jù)復(fù)制一份 創(chuàng)建新數(shù)據(jù)庫并修改sh_goods表增加字段is_seckill(是否秒殺商品)

在Admin后臺創(chuàng)建Goods控制器seckillConfig方法

//商品秒殺配置
public function seckillConfig()
{   
    #步驟2:加載視圖
    return $this->fetch('');
}

創(chuàng)建視圖文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>商品秒殺配置</title>
</head>
<body>
    <form action="" method="post">
        <p>秒殺開始時(shí)間<input type="text" name="start_time" /></p>
        <p>秒殺結(jié)束時(shí)間<input type="text" name="stop_time" /></p>
        <p>秒殺價(jià)格<input type="text" name="price" /></p>
        <p>秒殺數(shù)量<input type="text" name="num" /></p>
        <p><input type="submit" value="提交"></p>
    </form>
</body>
</html>

數(shù)據(jù)處理:修改商品狀態(tài)為秒殺 將秒殺商品數(shù)據(jù)保存的redis中

redis鍵規(guī)則:
hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3

<a href="{:url('admin/goods/seckillConfig', array('goods_id' => $list['goods_id']))}" 
class="showContent tablelink">
    秒殺商品配置
</a>

修改admin后臺的goods控制器sekillconfig方法進(jìn)行數(shù)據(jù)處理

  public function seckillConfig()
    {   
        #步驟1:判斷是否post提交
        if (request()->isPost()) {
            #步驟2:接受數(shù)據(jù)
            $start_time = input('start_time');
            $stop_time = input('stop_time');
            $price = input('price');
            $num = input('num');
            $goods_id = input('goods_id');
            #步驟3:插入數(shù)據(jù)
            $redis = new \Redis;
            $redis->connect('192.168.159.128', 6379);
            $redis->auth('123');
            #hmset 鍵  字段1 值1 ... 字段n 值n
            #hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3
            $tempData = array(
                'start_time' => $start_time,
                'stop_time' => $stop_time,
                'price' => $price,
                'real_num' => $num,
                'seckill_num' => $num,
            );
            $rs = $redis -> hMset('goods_seckill_'.$goods_id, $tempData);
            #步驟4:判斷
            if ($rs) {
                #修改商品狀態(tài)為秒殺
                Goods::where('goods_id', $goods_id)->update([
                    'is_seckill' => 1
                ]);
                #跳轉(zhuǎn)到商品秒殺列表頁
                $this->success("商品秒殺配置成功", url("admin/goods/seckill"));
            }else{
                $this->error("商品秒殺配置失敗");
            }
        } else {
            #步驟2:加載視圖
            return $this->fetch('');
        }
    }

整合日期插件

下載jq插件包放到查念public/plugin目錄好

商品秒殺頁配置引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>商品秒殺配置</title>
</head>
<body>
    <form action="" method="post">
        <p>秒殺開始時(shí)間
            <input type="text" name="start_time" 
                onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd HH:mm:ss'})" 
                autocomplete="off"
            />
        </p>
        <p>
            秒殺結(jié)束時(shí)間<input type="text" name="stop_time" 
                onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd HH:mm:ss'})" 
                autocomplete="off"
            />
        </p>
        <p>秒殺價(jià)格<input type="text" name="price" /></p>
        <p>秒殺數(shù)量<input type="text" name="num" /></p>
        <p><input type="submit" value="提交"></p>
    </form>

    <script language="javascript" type="text/javascript" src="/plugin/My97DatePicker/WdatePicker.js"></script>
</body>
</html>

修改控制器方法格式化日期

start_time => strtltime($start_time)
stop_time => strtotime($stop_time)

在Admin后臺創(chuàng)建Goods控制器seckill方法

public function seckill()
{
    #步驟1:查詢所有數(shù)據(jù)
    $seckills = Goods::where('is_seckill', 1)->select();
    #步驟2:過濾數(shù)據(jù)
    foreach ($seckills as $seckill) {
        #$seckill->goods_id
        #$seckill->goods_name
        #查詢商品秒殺信息
        $redis = new \Redis;
        $redis->connect('192.168.159.128', 6379);
        $redis->auth('123');
        $temp = $redis -> hMget('goods_seckill_'.$seckill->goods_id, array(
            'start_time',
            'stop_time',
            'price',
            'real_num'
        ));
        #將商品秒殺信息添加到$seckill中
        $seckill->start_time = $temp['start_time'];
        $seckill->stop_time = $temp['stop_time'];
        $seckill->price = $temp['price'];
        $seckill->real_num = $temp['real_num'];
    }
    #步驟3:加載視圖
    return $this->fetch('', [
        'seckills'=>$seckills
    ]);
}

創(chuàng)建視圖并循環(huán)顯示數(shù)據(jù)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <table border="1" cellpadding="10" cellspacing="0">
        <tr>
            <td>商品ID</td>
            <td>商品名稱</td>
            <td>商品價(jià)格</td>
            <td>商品數(shù)量</td>
            <td>開始時(shí)間</td>
            <td>結(jié)束時(shí)間</td>
            <td>距離結(jié)束</td>
        </tr>
        {foreach $seckills as $seckill}
        <tr>
            <td>{$seckill.goods_id}</td>
            <td>{$seckill.goods_name}</td>
            <td>{$seckill.price}</td>
            <td>{$seckill.real_num}</td>
            <td>{:date('Y-m-d H:i:s', $seckill.start_time)}</td>
            <td>{:date('Y-m-d H:i:s', $seckill.stop_time)}</td>
            <td>0</td>
        </tr>
        {/foreach}
    </table>
</body>
</html>

距離倒計(jì)時(shí)(修改控制器 ) 增加字段

$seckill->time = $temp['stop_time']

距離倒計(jì)時(shí)(修改視圖)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="{:config('admin_static')}/js/jquery.js"></script>
    <script>
    function timer(intDiff,idName) {
        window.setInterval(function() {
            var day = 0,
                hour = 0,
                minute = 0,
                second = 0; //時(shí)間默認(rèn)值     
            if (intDiff > 0) {
                day = Math.floor(intDiff / (60 * 60 * 24));
                hour = Math.floor(intDiff / (60 * 60)) - (day * 24);
                minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);
                second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
            }
            if (minute <= 9) minute = '0' + minute;
            if (second <= 9) second = '0' + second;

            $(idName).html(day + "天" + hour + '時(shí)' + minute + '分' + second + '秒');

            //console.log(idName);
            //$(idName+' .day_show').html(day + "天");
            //$(idName+' .hour_show').html('<s id="h"></s>' + hour + '時(shí)');
            //$(idName+' .minute_show').html('<s></s>' + minute + '分');
            //$(idName+' .second_show').html('<s></s>' + second + '秒');
            intDiff--;
        }, 1000);
    }
    </script>
</head>
<body>

    <table border="1" cellpadding="10" cellspacing="0">
        <tr>
            <td>商品ID</td>
            <td>商品名稱</td>
            <td>商品價(jià)格</td>
            <td>商品數(shù)量</td>
            <td>開始時(shí)間</td>
            <td>結(jié)束時(shí)間</td>
            <td>距離結(jié)束</td>
        </tr>
        {foreach $seckills as $seckill}
        <tr>
            <td>{$seckill.goods_id}</td>
            <td>{$seckill.goods_name}</td>
            <td>{$seckill.price}</td>
            <td>{$seckill.real_num}</td>
            <td>{:date('Y-m-d H:i:s', $seckill.start_time)}</td>
            <td>{:date('Y-m-d H:i:s', $seckill.stop_time)}</td>
            <td id="time{$seckill.goods_id}">0</td>
        </tr>
        <script>
         timer({$seckill->time},'#time{$seckill.goods_id}');
        </script>
        {/foreach}
    </table>
</body>
</html>

完成前臺秒殺功能

修改home/index/index

<li><a href="{:url('home/seckill/index')}">商品秒殺</a></li>

在后臺創(chuàng)建seckill控制器index方法

<?php
namespace app\home\controller;
use think\Controller;
use app\home\model\Goods;

class SeckillController extends Controller
{
    public function index()
    {
      #步驟1:查詢所有數(shù)據(jù)
      $seckills = Goods::where('is_seckill', 1)->select();
      #步驟2:過濾數(shù)據(jù)
      foreach ($seckills as $seckill) {
          #$seckill->goods_id
          #$seckill->goods_name
          #查詢商品秒殺信息
          $redis = new \Redis;
          $redis->connect('192.168.159.128', 6379);
          $redis->auth('123');
          $temp = $redis -> hMget('goods_seckill_'.$seckill->goods_id, array(
              'start_time',
              'stop_time',
              'price',
              'real_num',
              'seckill_num',
          ));
          #將商品秒殺信息添加到$seckill中
          $seckill->start_time = $temp['start_time'];
          $seckill->stop_time = $temp['stop_time'];
          $seckill->price = $temp['price'];
          $seckill->real_num = $temp['real_num'];
          $seckill->seckill_num = $temp['seckill_num'];
      }
      #步驟3:加載視圖
      return $this->fetch('', [
          'seckills'=>$seckills
      ]);
    }
}

創(chuàng)建視圖并循環(huán)顯示數(shù)據(jù)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>一元秒殺!還包郵!</title>
<style type="text/css">
*{margin:0; padding:0;}
body{font-family:"微軟雅黑";}
.html{ overflow:auto}
.c{ clear:both;}
a{text-decoration: NONE; color:#000; font-size:16px;}
.bodybg{ background:url({:config('home_static')}/image/bg.jpg); width:100%;}
.banner{ background:url({:config('home_static')}/image/banner.jpg) no-repeat center; height:542px;}
.nav{ border:none;width:1000px;margin:0 auto;}
.probg{ background:#f4ad1e; width:1000px; margin:0 auto; text-align:center;}
.fl{ float:left; padding-left:9px; padding-top:9px;}
.onebg{ width:321px; height:321px; background:#FFF;}
.titlebg{ width:321px; height:33px; background:#FFF;}
.buybg{ background:url({:config('home_static')}/image/pricebg.jpg); width:321px; height:70px;} 
.foreprice{ font-size:17px; text-decoration:line-through; color:#FFF;float:left; padding-top:13px; padding-left:25px;}
.button{ width:96px;height:70px;float:right; padding:0;}
.ads{ width:1000px; height:326px; margin:0 auto;}
.sm{ background:#FFF; width:100%; margin:0 auto;}
.xize{ width:1000px; margin:0 auto; text-align:left; font-size:36px; font-weight:700; color:#9E051A;}
.shuoming{ width:1000px; margin:0 auto; text-align:left; font-size:20px;color:#9E051A; line-height:35px;}
</style>
</head>

<body>
    <div class="banner"></div>
    <div class="bodybg">
            <div class="nav"><img src="{:config('home_static')}/image/nav.jpg" width="1000" height="70"/></div>
            <div class="probg"> 

           {foreach $seckills as $seckill}
             <!-- 單個(gè)商品循環(huán)-->
              <div class="fl">
                <div class="onebg"  >
                <a href="javascript:void(0);" ><img src="{:config('home_static')}/image/1.jpg" width="321px;height:321px"/></a>
                </div> 
                <div class="titlebg">
                <a href="javascript:void(0);" >{$seckill.goods_name}</a>
                </div>
                <div class="buybg" style="background: red;">
                <div class="foreprice" style="text-decoration:none;">價(jià)格:{$seckill.price}</div>
                <div class="foreprice" style="text-decoration:none;">庫存:{$seckill.seckill_num}</div>
                <div class="button">
                    <!-- <img src="{:config('home_static')}/image/button.jpg"/> -->
                    <input type="button" value="立即搶購"  style="margin-top:20px; width: 80px;height: 30px; cursor: pointer;" data-id="{$seckill.goods_id}"/>
                  </div>        
                </div>
              </div>
              <!-- 單個(gè)商品循環(huán)結(jié)束-->
           {/foreach}

              <div>
                <a href="javascript:void(0);" ><img src="{:config('home_static')}/image/ads.jpg" /></a>
              </div>
              
              <div class="c"></div>                      
         </div> 
         <div class="sm">
         <br />
             <div class="xize">1元秒殺細(xì)則:</div>
            <div class="shuoming">1.參與秒殺前,請?jiān)敿?xì)閱讀秒殺規(guī)則,凡參與1元秒殺活動的用戶,均視為同意秒殺規(guī)則。<br />2.秒殺商品將于2014年7月7日08:00:00上線-2014年7月11日23:59:59結(jié)束,當(dāng)天商品售罄時(shí)當(dāng)天秒殺結(jié)束,活動期間每一個(gè)云中央注冊會員每期僅限秒殺一個(gè)商品,秒殺多件成功者,并通過收貨人及聯(lián)系方式可判定為同一人的,則取消全部訂單。<br />3.秒殺成功以支付成功為準(zhǔn),早秒早得;秒殺下單后30分鐘內(nèi)未付款者自動取消訂單,請?zhí)貏e注意。<br />4.請確保秒殺填寫的收貨人信息真實(shí)有效,因聯(lián)系方式填寫錯(cuò)誤導(dǎo)致未收到禮品的,由用戶自行承擔(dān)損失。<br />5.對于任何通過不正當(dāng)手段參與秒殺者,不正當(dāng)手段包括但不限于使用秒殺器或類似作弊軟件,云中央網(wǎng)站有權(quán)依據(jù)自身技術(shù)判斷,并在不事先通知的情況下取消其秒殺資格或者取消訂單。
             </div>
         </div>
    </div>
</body>
</html>

立即搶購(入隊(duì))

在home/平臺創(chuàng)建Seckill控制器創(chuàng)建add方法

public function add()
{
    #步驟1:接受數(shù)據(jù)
    $uid = session('member_id');
    if(!$uid) {
      echo json_encode(array('state' => 0, 'msg'=>'請登錄后重試...'));
      die;
    };
    $goods_id = input('goods_id');
    if(!$goods_id) {
      echo json_encode(array('state' => 0, 'msg'=>'非法操作...'));
      die;
    }

    #步驟2:連接Redis
    $redis = new \Redis;
    $redis->connect('192.168.159.128', 6379);
    $redis->auth('123');
    $redis->select(0);

    #步驟3:過濾(判斷時(shí)間和庫存)
    //獲取商品信息
    $goodsInfo = $redis->hmget("goods_seckill_{$goods_id}", array(
      'start_time', 'stop_time', 'real_num', 'seckill_num', 'price'
    ));
    if(!$goodsInfo) {
      echo json_encode(array('state' => 0, 'msg'=>'秒殺商品不存在...'));
      die;
    }
    //判斷是否開始
    if ($goodsInfo['start_time'] > time()) {
      echo json_encode(array('state' => 0, 'msg'=>'未開始'));
      die;
    }
    //判斷是否結(jié)束
    if ($goodsInfo['stop_time'] < time()) {
      echo json_encode(array('state' => 0, 'msg'=>'已結(jié)束'));
      die;
    }
    //判斷庫存
    if ($goodsInfo['seckill_num'] < 1) {
      echo json_encode(array('state' => 0, 'msg'=>'對不起,寶貝已被搶完!'));
      die;
    }

    #步驟4:將用戶請求加入消息隊(duì)列中
    $len = $redis->lpush("goods_seckill_{$goods_id}_rs", $uid.'%'.$goods_id.'%'.$goodsInfo['price']);

    #步驟5:判斷庫存(規(guī)則:隊(duì)列中前n個(gè)搶購成功)
    if ($len > $goodsInfo['real_num']) {
    //搶購失敗(隊(duì)列長度 > 庫存)
      echo json_encode(array('state' => 0, 'msg'=>'對不起,寶貝已被搶完!'));
      die;
    } else {
    //搶購成功,減庫存(注:千萬不能直接操作mysql因?yàn)橛胁l(fā)限制)
      echo json_encode(array('state' => 1, 'msg'=>'秒殺成功'));
      die;
    }
}

修改秒殺列表發(fā)送異步請求

<script type="text/javascript" src="{:config('home_static')}/js/jquery-1.8.3.min.js"></script>
<script> 
$(function(){
    $('.buybg input').click(function(){
         //禁用按鈕
         var thisObj = $(this);
         $(this).attr('disabled', 'disabled');
         $(this).css('cursor', 'default');
         $(this).val('搶購中...');

         var goods_id = $(this).attr('data-id');
         $.post("{:url('home/seckill/add')}", {goods_id:goods_id},function(data){
              if (data.state) {
                alert(data.msg);
                location.href = "{:url('home/order/seckill')}";
              } else {
                alert(data.msg)
              }

              //還原按鈕
             $(thisObj).removeAttr('disabled');
             $(thisObj).css('cursor', 'pointer');
             $(thisObj).val('立即搶購');

         }, 'json');

    });
});
</script>

使用Workerman框架(出隊(duì))

<?php
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';

$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
    //每0.1秒執(zhí)行一次(精度可以達(dá)到毫秒0.001)
    $time_interval = 0.1; 
    Timer::add($time_interval, function()
    {
        $goods_id = 9;
        #步驟1:連接Redis
        $redis = new Redis;
        $redis->connect('192.168.159.128', 6379);
        $redis->auth('123');
        $redis->select(0);
        #步驟2:獲取秒殺相關(guān)信息
        $allowBuyNum = $redis->hget("goods_seckill_{$goods_id}", 'seckill_num'); //秒殺剩余庫存
        $orderInfoString = $redis->rpop("goods_seckill_{$goods_id}_rs");                     //隊(duì)列搶購用戶信息
        #步驟3:判斷(有庫存 && 有人搶購)
        if($allowBuyNum > 0 && $orderInfoString) 
        {
            echo "allowBuyNum:$allowBuyNum\n";

            #步驟4:減庫存
            $redis->hincrby('goods_seckill_1', 'seckill_num', -1);
            #步驟5:生成訂單
            $pdo = new \PDO('mysql:dbname=seckill', 'root', 'root');

            $userOrderInfo = explode('%', $orderInfoString); //$uid.'%'.$goods_id.'%'.$price
                $order_id = date('Ymd').time().uniqid();
                $total_price = $userOrderInfo[2];
                $member_id = $userOrderInfo[0];
                $goods_id = $userOrderInfo[1];
                $create_time = time();
                $update_time = time();
                #主表(sh_order)
                $sql = "insert into sh_order (is_seckill, order_id, total_price, member_id, create_time, update_time)
                value 
                (1, '{$order_id}', $total_price, $member_id, $create_time, $update_time)";
                $pdo->exec($sql);
                #從表(sh_order_goods)
                $sql = "insert into sh_order_goods (order_id, goods_id, goods_number, goods_price) value('{$order_id}', $goods_id, 1, $total_price)";
                $pdo->exec($sql);
                
            echo "over...\n";
        }
    });
};

// 運(yùn)行worker
Worker::runAll();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容