基于Redis的秒杀

随着电商业务的发展,秒杀是个十分常见的场景,今天我们来利用Redis实现一个简单的秒杀系统。

假定我们有一个商品id为1,秒杀数量是5。

一般场景:

include 'db.php';
$db = new db([
    'database_type' => 'mysql',
    'database_name' => 'test',
    'server' => 'www.puresai.com',
    'username' => 'puresai',
    'password' => '*',
    'charset' => 'utf8'
]);

$stock_num= $db->get('goods', 'stock_num', ['id' => 1]);

// 检测库存
if ($stock_num> 0) {
    sleep(1); //模拟真实环境
    $db->update('goods', ["stock_num[-]" => 1], ['id' => 1]);
    print_r('ok')
} else {
    print_r('sorry')
}

我们尝试模拟高并发场景,使用ab压测工具,

ab -n 500 -c 500 http://www.puresai.com/test/miaosha.php

运行后发现库存stock_num很可能已经变成负数了,出现了超卖问题。

引入Redis

//实例化Redis
$Redis = new Redis();
//连接
$Redis->connect('127.0.0.1', 6379);
$key = 'sale';
//检测是否连接成功
// echo "Server is running: " . $Redis->ping();
$Redis->setnx($key, 0);
$Redis->watch($key); //监测一个key的值是否被更改

$sale_num = $Redis->get($key);
if ($sale_num > 4) {
    exit();
}

$Redis->multi(); //标记事务
$Redis->incr($key);  //销量+1
sleep(1); //模拟真实环境
$ret = $Redis->exec(); // 事务块内所有命令的返回值,按命令执行的先后顺序排列。
if ($ret) {
    include 'db.php';
    $db = new db([
        'database_type' => 'mysql',
        'database_name' => 'test',
        'server' => 'www.puresai.com',
        'username' => 'puresai',
        'password' => '*',
        'charset' => 'utf8'
    ]);
    $db->update('goods', ["stock_num[-]" => 1], ['id' => 1]);
}

重新增加库存到5,多次测试,发现库存并无出现负数情况,测试通过。


基于Redis的秒杀
https://blog.puresai.com/2018/08/09/165/
作者
puresai
许可协议