Redis实现高并发下的抢购,秒杀,解决方案

来源:互联网 发布:linux对文件内容排序 编辑:程序博客网 时间:2024/05/10 20:42

在电子商务发达的今天,各种秒杀,抢购活动的场景不少,如何解决高并发下出现的订单超发情况呢?

在常规情况下,我们会根据用户提交的请求去查询商品库存,如果库存小于0则订单生成失败。但是这种情况下经常了订单需要的数量超过了库存数量,即出现负库存。

先讨论常规场景测试:

我们在redis中设置一个库存表,就是个简单的string类型, 用以标示库存即可。 

set inventory 100

使用多进程测试如下:因为我的AB测试不能用。所以使用多进程的方式进行

//register a signalpcntl_signal(SIGCHLD, SIG_IGN);$times = 200;while ($times-- > 0) {    $pid = pcntl_fork();    if ($pid > 0) {    } else {        order();        die;    }}sleep(20);function order() {    $conn = new Redis;    //connect redis    $conn->connect("127.0.0.1", 6379);    $inventory = $conn->get('inventory');    //由于本地服务过访问过快。所以休息50毫秒真实模拟高并发    usleep(50000);    if ($inventory > 0) {        $conn->decr("inventory");    } else {        echo "抢购失败!";    }    die;}

执行结果变为了负数:



如何解决这个问题呢? 熟悉redis 的同学都知道 redis 支持事务,我们加个事务试试。


<?php//register a signalpcntl_signal(SIGCHLD, SIG_IGN);$times = 200;while ($times-- > 0) {    $pid = pcntl_fork();    if ($pid > 0) {    } else {        order();        die;    }}sleep(20);function order() {    $conn = new Redis;    //connect redis    $conn->connect("127.0.0.1", 6379);    $inventory = $conn->get('inventory');    $conn->multi();    //由于本地服务过访问过快。所以休息50毫秒真实模拟高并发    usleep(50000);    if ($inventory > 0) {        $conn->decr("inventory");        $conn->exec();    } else {        echo "抢购失败!";    }    die;}?>

同样结果还是会变成负数。 


接下来可以使用redis 的watch 实现一个乐观锁来试试

<?php//register a signalpcntl_signal(SIGCHLD, SIG_IGN);$times = 200;while ($times-- > 0) {    $pid = pcntl_fork();    if ($pid > 0) {    } else {        order();        die;    }}sleep(20);function order() {    $conn = new Redis;    //connect redis    $conn->connect("127.0.0.1", 6379);    do {        //watch inventory        $conn->watch('inventory');        $inventory = $conn->get('inventory');        //由于本地服务过访问过快。所以休息50毫秒真实模拟高并发        usleep(50000);        if ($inventory <= 0) {            echo "抢购失败!";            break;        }        $conn->multi();        $conn->decr("inventory");    } while ($conn->exec());    die;}?>

执行结果:

大致场景就是这样的, 如果是抢购,每次只抢购一个 可以往队列里添加库存个商品的1 队列, 如图:


当用户每次抢购抛出一个队列,然后再去减少真正的库存即可。  当然真实的场景要稍复杂一些, 不过原理就是这样的。 


成功!


阅读全文
0 0
原创粉丝点击