php redis实现秒杀
PHP Redis 实现秒杀的关键技术
秒杀场景的核心是高并发下的数据一致性和性能问题。Redis 因其高性能和原子性操作成为实现秒杀的重要工具。
使用 Redis 原子操作
通过 Redis 的 DECR 或 INCRBY 命令实现库存扣减,确保原子性:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$stockKey = 'seckill:item:123:stock';
// 检查库存并扣减
if ($redis->decr($stockKey) >= 0) {
// 扣减成功,生成订单
} else {
// 库存不足
$redis->incr($stockKey); // 回滚
}
Lua 脚本保证原子性
复杂逻辑可通过 Lua 脚本实现多命令原子执行:

$lua = <<<LUA
local stockKey = KEYS[1]
local userId = ARGV[1]
local limitKey = 'seckill:item:'..stockKey..':limit'
-- 检查是否已参与
if redis.call('sismember', limitKey, userId) == 1 then
return 0
end
-- 扣减库存
if redis.call('decr', stockKey) >= 0 then
redis.call('sadd', limitKey, userId)
return 1
else
redis.call('incr', stockKey)
return -1
end
LUA;
$result = $redis->eval($lua, ['seckill:item:123:stock', $userId], 1);
防止超卖的措施
预减库存:系统启动时加载库存到 Redis,所有扣减在 Redis 完成,最后异步同步到数据库。
限流措施:使用 Redis 计数器实现简单限流:

$rateLimitKey = 'seckill:rate:' . date('Hi');
if ($redis->incr($rateLimitKey) > 1000) {
// 超过限流阈值
}
$redis->expire($rateLimitKey, 60);
队列削峰
使用 Redis 的 List 结构作为队列缓冲请求:
$queueKey = 'seckill:queue';
// 生产者
$redis->lpush($queueKey, json_encode(['user_id' => $userId, 'item_id' => $itemId]));
// 消费者
while (true) {
$data = $redis->rpop($queueKey);
if ($data) {
// 处理秒杀逻辑
}
}
数据预热与缓存
提前加载商品数据到 Redis,避免高峰期数据库访问:
$itemData = [
'stock' => 100,
'price' => 99,
'start_time' => strtotime('2023-06-01 20:00:00')
];
$redis->hmset('seckill:item:123', $itemData);
分布式锁处理
防止重复处理使用 Redis 分布式锁:
$lockKey = 'seckill:lock:' . $itemId;
$token = uniqid();
if ($redis->set($lockKey, $token, ['NX', 'EX' => 10])) {
try {
// 处理业务
} finally {
if ($redis->get($lockKey) == $token) {
$redis->del($lockKey);
}
}
}
以上方法组合使用可构建完整的秒杀系统,注意根据实际场景调整细节,如添加重试机制、完善错误处理等。






