php redis实现秒杀
使用 PHP 和 Redis 实现秒杀功能
秒杀场景的核心是高并发下的库存控制和防止超卖。Redis 因其高性能和原子操作特性,非常适合实现秒杀系统。
基于 Redis 原子操作实现
使用 Redis 的 DECR 或 INCRBY 命令配合 WATCH 实现原子性库存扣减:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$productId = 'product_123';
$userId = uniqid();
// 初始化库存
$redis->set($productId . '_stock', 100);
// 秒杀逻辑
$redis->watch($productId . '_stock');
$stock = $redis->get($productId . '_stock');
if ($stock > 0) {
$redis->multi();
$redis->decr($productId . '_stock');
$result = $redis->exec();
if ($result) {
$redis->sAdd($productId . '_success', $userId);
echo "秒杀成功";
} else {
echo "秒杀失败,请重试";
}
} else {
$redis->unwatch();
echo "库存不足";
}
使用 Lua 脚本保证原子性
Lua 脚本在 Redis 中执行是原子的,更适合高并发场景:

$lua = <<<LUA
local stock_key = KEYS[1]
local success_key = KEYS[2]
local user_id = ARGV[1]
local stock = tonumber(redis.call('GET', stock_key))
if stock and stock > 0 then
redis.call('DECR', stock_key)
redis.call('SADD', success_key, user_id)
return 1
end
return 0
LUA;
$result = $redis->eval($lua, [$productId.'_stock', $productId.'_success', $userId], 2);
if ($result) {
echo "秒杀成功";
} else {
echo "秒杀失败";
}
防止重复购买
使用 Redis 集合记录成功用户,避免重复购买:
if ($redis->sIsMember($productId.'_success', $userId)) {
echo "您已经参与过秒杀";
exit;
}
限流措施
使用 Redis 实现简单限流,控制访问频率:

$ip = $_SERVER['REMOTE_ADDR'];
$key = 'limit:' . $ip;
$limit = 5; // 5次/秒
if ($redis->get($key) >= $limit) {
echo "访问过于频繁";
exit;
}
$redis->incr($key);
$redis->expire($key, 1);
异步处理订单
秒杀成功后,将订单信息放入队列异步处理:
if ($result) {
$orderData = [
'user_id' => $userId,
'product_id' => $productId,
'time' => time()
];
$redis->rPush('order_queue', json_encode($orderData));
}
库存预热
提前将库存加载到 Redis,避免秒杀开始时的数据库压力:
// 从数据库获取库存
$stockFromDB = 100; // 假设从数据库查询得到
$redis->set($productId.'_stock', $stockFromDB);
注意事项
- 使用连接池管理 Redis 连接,避免频繁创建连接
- 监控 Redis 性能,确保能够承受预期流量
- 考虑使用 Redis 集群提高可用性
- 秒杀结束后同步 Redis 和数据库的库存数据
- 前端应配合使用验证码、按钮禁用等措施防止重复提交






