php redis 秒杀

本文介绍了如何使用PHP和Redis实现秒杀场景的分布式锁,通过乐观锁和行级锁的方式控制并发,确保库存安全。通过实例代码演示了llen、lpush、lpop、incr等操作,并提供了压力测试和不同锁策略的对比分析。
摘要由CSDN通过智能技术生成

                                      php redis 秒杀

 鉴于网上很多关于redis的,用的函数也不一样,很多函数重复的。我自己参考了几个,并实际测验给大家分享一下。


test库  goods表

CREATE TABLE `goods` (
  `good_id` int(11) NOT NULL AUTO_INCREMENT,
  `num` int(11) DEFAULT NULL,
  PRIMARY KEY (`good_id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

 

基于redis分布式锁实现“秒杀

是短时间内多个用户“争抢”资源,秒杀就是多个线程对资源进行操作,所以实现秒杀,就必须控制线程对资源的争抢,既要保证高效并发
我们来假设一个最简单的秒杀场景:数据库里有一张表,column分别是商品ID,和商品ID对应的库存量,秒杀成功就将此商品库存量-1。现在假设有1000个线程来秒杀两件商品,500个线程秒杀第一个商品,500个线程秒杀第二个商品。我们来根据这个简单的业务场景来解释一下分布式锁。
通常具有秒杀场景的业务系统都比较复杂,承载的业务量非常巨大,并发量也很高。这样的系统往往采用分布式的架构来均衡负载。那么这1000个并发就会是从不同的地方过来,商品库存就是共享的资源,也是这1000个并发争抢的资源,这个时候我们需要将并发互斥管理起来。这就是分布式锁的应用。 

而key-value存储系统,如redis,因为其一些特性,是实现分布式锁的重要工具。

 

先普及一下用到的知识点

llen
获取list的元素个数

lpush
添加一个或多个元素插入到list的头部

$redis->get("num")
(一开始默认为0,证明还没有人参与抢购。后续逐渐自增)

$redis->lpush("user",1);//记录抢购成功的用户id
$redis->lpush("user",2);//记录抢购成功的用户id
var_dump($redis->lrange('user',0,-1));
array(2) { [0]=> string(1) "2" [1]=> string(1) "1" }


开启wins本地redis服务端
redis-server.exe redis.windows.conf
开启客户端
D:\download\install\redis\redis>redis-cli.exe
127.0.0.1:6379> auth "12345"

lpop
描述:返回和移除列表的第一个元素
参数:key
返回值:成功返回第一个元素的值 ,失败返回false

//获取列表中所有的值
$list = $redis->lrange('list', 0, -1);

自增
$redis->incr('num')

使用ab进行压力测试详解
https://blog.csdn.net/qiu1988yang/article/details/74940914
测试总次数为1000,并发数为100(相当于100个用户同时访问,他们总共访问1000次)。我们输入DOS命令
ab -n 1000 -c 100  http://www.testone.cn/redis.php

在这个测试的过程中,我一边遍用ab测试,一边访问浏览器,因为超卖有时候不会那么明显发生,再一个如果超过ab测试范围(我是用本地phpStudy自带的apache测试的)就无法访问了,造成测试失败

测试总次数为1000,并发数为100(相当于100个用户同时访问,他们总共访问1000次)

第一种

推荐方式 php+redis乐观锁(如果你不了解redis的各个函数意思,请不要乱排序这几行代码,因为我试着改了几行发现数据库直接变成-9,所以你看顺序的话先不要变。以实现功能为主,以后慢慢理解)

<?php   
$pdo=new PDO("mysql:dbname=test;host=localhost","root","root");
$pdo->query('SET NAMES utf8');
$redis=new Redis();
$redis->connect('127.0.0.1', 6379); 
$redis->auth('12345'); 
$uid=md5(mt_rand(1,1000000));//模拟生成用户id
// $redis->lpush("user",1);//记录抢购成功的用户id
// $redis->lpush("user",2);//记录抢购成功的用户id
// var_dump($redis->lrange('user',0,-1));
//array(2) { [0]=> string(1) "2" [1]=> string(1) "1" }

$num=$redis->get("num");
if($num>=3){
   exit("抢购结束");//假设只有3个库存量 
}else{
    $redis->watch('num');//监听key的变化,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
    //开始事务
    $redis->multi(); 
    $redis->incr('num'); 
    $result=$redis->exec(); //返回true表示执行成功,nil表示事务被中断
    if($result)
    {  
        $stmt = $pdo->exec("update goods set num=num-1 where good_id=1");
        if($stmt){
            echo "抢购成功";
            $redis->lpush("user",$uid);//记录抢购成功的用户id
            //自行写各种业务处理。。。
     
        }
    }
 

第二种  

行级锁
<?php   
$pdo=new PDO("mysql:dbname=test;host=localhost","root","root");
$pdo->query('SET NAMES utf8');
$redis=new Redis();
$redis->connect('127.0.0.1', 6379); 
$redis->auth('12345'); 
$num=$redis->get("num");

if($num<=3)//假定库存只有3个
{   
    $sql = "select * from goods where good_id=1";
    $stmt = $pdo->query($sql);
    $res=$stmt->fetch(2);
    if($res['num'])
    {   
          var_dump(1);
        $redis->incr('num');
        $stmt = $pdo->exec("update goods set num=num-1 where good_id=1");
        //原作者写的是下面这个,但是我实际测试的时候这里不加行锁也没有问题。用的上面这句。不过具体还要参照自己的业务情况去分析。
        $stmt = $pdo->exec("update goods set num=num-1 where good_id=1 for update"");
        echo "抢购成功";exit;
    //自行写各种业务处理。。。
    }
}else{
    var_dump(2);
    echo "抢购结束";exit;
}


如果对行锁不懂可参考这个
MySQL的for update使用(行级锁)
https://blog.csdn.net/qq_27037443/article/details/102984108


第三种
文件锁不推荐就不说了


第四种
直接上网址吧https://www.cnblogs.com/yehuisir/p/10776172.html
这个里面文章写得思路很详细,但是我没有实际测试。


第五种

将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false(未测试)
其他的包括很多种其他方式,我没有测试就不说了。

参考网址:
MySQL的for update使用(行级锁)

https://blog.csdn.net/qq_27037443/article/details/102984108

https://www.cnblogs.com/jokmangood/p/11737169.html
php操作redis常用方法

redis本地服务启动和桌面客户端安装(Windows)
https://blog.csdn.net/liuchang19950703/article/details/104372940/

范例:
php结合redis实现高并发下的抢购、秒杀功能
https://www.cnblogs.com/huanglei559/p/11045516.html

PHP操作Redis数据库常用方法
https://www.cnblogs.com/lxwphp/p/10597809.html

Php+redis+锁机制实现高并发秒杀抢购解决方案
https://blog.csdn.net/qq_39418742/article/details/105974159


PHP使用Redis的Transaction(事务)命令
https://blog.csdn.net/weixin_43910923/article/details/88693412


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值