php redis decr_PHP 通过redis和mysql实现秒杀业务

第一种方式

$db = MySQLDB::getInstance();
$info = $db->fetchRow('select * from goods where goods_id=1');

//判断是否还有库存
if ($info['stock'] <= 0) {
 exit('卖完了');
}
 

//减少库存,num 只是一个记录修改数据的次数,可以判断是否存在超卖现象
$result = $db->update("update goods set stock=stock-1,num=num+1 where goods_id=1 and stock>0");
if (!$result) {
 exit('抢购失败');
}
file_put_contents('./order.txt', $result . PHP_EOL, FILE_APPEND);
exit('抢购成功');

通过apache压测工具ab来试一试会不会超卖

数据库情况

v2-73d3e591edbe0746129f1ccd76103037_b.jpg
ab -c 100 -n 1000 http://test.localhost.com/database.php

结果

数据库情况

v2-4558b25104295a4a445cc61ceb81689b_b.jpg

日志文件

v2-24470b3a428bd795acccaf0e999b3647_b.jpg

没有超卖

这种方案适合用户量少的情况,mysql压力不会太大

第二种方案

监听key + 事务 实现

具体代码

    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379, 5);

    //监听已抢购的数量,如果在事务执行之前这个 key 被其他命令所改动,那么事务将被打断。
    $redis->watch('rush:goods_stock:1');
    //获取库存
    $stock = $redis->get('rush:goods_stock:1');
    if ($stock == 0) {
       die('抢购失败,已经销售完毕');
    }
    //事务开始
    $redis->multi();
    //库存 -1
    $redis->decr('rush:goods_stock:1');
    //执行提交
    $res = $redis->exec();
    //事务提交成功
    if ($res) {
        $data = [
           'goods_id'=>1,
           'user_id'=>rand(1,9999),
           'time'=>microtime(true)
        ];
        $redis->lPush('rush:goods:1',json_encode($data));
        file_put_contents('./order.txt', 1 . PHP_EOL, FILE_APPEND);
        die('抢购成功');
    }
    die('抢购失败');

设置redis 的值

v2-e4a11e875838f056ab9086b1aebadaee_b.jpg

通过ab压测

ab -c 100 -n 1000 http://192.168.1.13:82/redis.php

查看redis数据情况

v2-3ec04a63989755dd2fde58def347d0d0_b.jpg

这种方案不一定是先提交的先下单,可能会涉及到还有库存抢购失败的情况。

第三种方案

通过把库存设置为redis 队列 来实现

先设置商品库存的队列


    $redis = new Redis();

    $redis->connect('127.0.0.1', 6379, 5);

 //库存

    $stock = 10;

 //先清空

    $redis->del('rush:goods_stock:queue:2');

 //放入队列中

 for ($i = 0; $i < $stock; $i++) {

        $redis->lPush('rush:goods_stock:queue:2', 1);

    }

 die('放入队列完成');

执行完成看一下redis数据

v2-0d97ecb79f86c4340f3a871b0124eaff_b.jpg

实现抢购功能


    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379, 5);
    //读取队列,redis操作都是原子性的,不用担心会重复读取
    if ($redis->rPop('rush:goods_stock:queue:2')) {
        $data = [
             'goods_id'=>2,
             'user_id'=>rand(1,9999),
             'time'=>microtime(true)
        ];
        $redis->lPush('rush:goods:2',json_encode($data));
        die('抢购成功');
    }
    die('抢购失败,已经销售完毕');

压测

ab -c 100 -n 1000 http://192.168.1.13:82/redis.php

结果数据正确

v2-305a4926b14faa17609c15dbc5b027e2_b.jpg

该方案用户基本可以先下手为强。注意设置库存的地方

库存解决好了后面就是把订单写入数据库,其实跑一个脚本去读抢购成功的队列就行了

有的文章是直接判断抢购成功的队列是否小于库存,并且写入队列的时候没有减少库存。这样的方式必须等待抢购完毕之后才能写入数据库,因为一边进一边出,会导致超卖,这种方式不太灵活。也看具体场景吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值