前段时间做了一个秒杀的功能,利用redis的高性能和事务特性,用于解决高并发情况下商品库存扣减,预防商品超卖的问题。
扣减库存的过程,主要用到了redis的watch、multi、exec等命令。
watch 命令用于监视一个或多个key,监视事务执行过程key是否有被其他命令修改,如果key有被修改,事务将被打断,事务中涉及的修改会被回滚。
multi 命令用于开始事务。
exec 命令用于执行事务,如果事务执行成功,返回命令的执行结果;如果事务被中断,返回false。
/*
* 秒杀扣减库存
*/
function reduceStock($userId, $productId)
{
// 初始化redis
$redis = …
// 商品库存key
$stockKey = 'seckill:' . $productId . 'stock:';
$stock = $redis->get($stockKey);
if ($stock <= 0) {
// 商品库存小于等于0,商品已被抢完
return false;
}
$memberKey = 'seckill:' . $productId . ':uid:' . $userId;
if ($redis->sIsMember($memberKey)) {
// 该用户已参与过该商品本次秒杀活动
return false;
}
// 使用watch命令监视一个或多个key
// 如果在事务执行之前,被监视的key如果又被其他命令修改
// 那么事务将被打断,事务中涉及的修改会被回滚
$redis->watch($stockKey);
// 调用命令multi开始事务
$redis->multi();
$redis->decr($stockKey); // 减库存
$redis->sAdd($memberKey, $userId); // 将用户加入已参见活动的集合
// 调用命令exec执行事务
$result = $redis->exec();
$redis->close(); // 关闭redis连接
if (!$result) {
// 执行事务失败,扣减库存失败
return false;
}
// 执行事务失败,扣减库存成功
return true;
}
Categories: PHP