php redis实现秒杀
Redis实现秒杀的PHP方案
秒杀场景的核心问题是高并发下的超卖和性能瓶颈。Redis凭借单线程原子性和高性能特性,是解决这一问题的理想选择。以下是基于PHP和Redis的完整实现方案:
使用Redis原子操作防止超卖
通过Redis的原子性操作(如DECR、INCRBY)确保库存扣减的准确性:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$productId = 'product_123';
$userId = uniqid();
// Lua脚本保证原子性
$lua = <<<LUA
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECR', KEYS[1])
return redis.call('SADD', KEYS[2], ARGV[1])
end
return 0
LUA;
$result = $redis->eval($lua, [$productId, 'seckill_success_users', $userId], 2);
if ($result) {
echo "秒杀成功";
} else {
echo "秒杀失败";
}
库存预热与限流措施
提前将商品库存加载到Redis,避免活动开始时的数据库压力:
// 活动开始前执行
$totalStock = 1000;
$redis->set('product_123', $totalStock);
$redis->expire('product_123', 3600); // 设置过期时间
结合令牌桶算法实现限流:
function acquireToken($redis, $key, $limit, $expire) {
$now = microtime(true);
$redis->watch($key);
$lastTime = $redis->get($key);
if ($lastTime && $now - $lastTime < 1/$limit) {
$redis->unwatch();
return false;
}
$redis->multi();
$redis->set($key, $now);
$redis->expire($key, $expire);
return $redis->exec();
}
异步订单处理方案
秒杀成功后采用消息队列异步处理订单:
// 秒杀成功后
if ($result) {
$orderData = [
'user_id' => $userId,
'product_id' => $productId,
'create_time' => time()
];
$redis->lPush('seckill_order_queue', json_encode($orderData));
}
后台消费进程处理:
while (true) {
$orderJson = $redis->rPop('seckill_order_queue');
if ($orderJson) {
$order = json_decode($orderJson, true);
// 实际订单入库操作
saveToDatabase($order);
}
usleep(100000); // 100ms间隔
}
数据一致性保障
采用双重检查避免数据库与Redis数据不一致:
// 下单前再次检查
if ($redis->sIsMember('seckill_success_users', $userId)) {
$dbStock = getDbStock($productId);
if ($dbStock > 0) {
createOrder($userId, $productId);
} else {
$redis->sRem('seckill_success_users', $userId);
$redis->incr('product_123'); // 回滚库存
}
}
性能优化关键点
-
使用Redis管道减少网络往返:
$redis->pipeline(); $redis->get('stock'); $redis->sAdd('users', $userId); $results = $redis->exec(); -
连接池管理避免频繁创建连接
-
热点数据分片存储减轻单节点压力
-
采用CLUSTER模式实现横向扩展

该方案通过Redis原子操作保证数据一致性,异步处理提升系统吞吐量,多重校验确保最终准确性,可支持万级QPS的秒杀场景。实际部署时需要根据具体业务调整参数并进行压力测试。






