php 秒杀实现
秒杀系统设计要点
高并发、低延迟、数据一致性是秒杀系统的核心挑战。PHP实现需结合缓存、队列、数据库优化等技术。
数据库设计优化
创建专门的秒杀商品表,包含库存、开始/结束时间等字段。使用InnoDB引擎确保事务支持。
CREATE TABLE `seckill_goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stock` int(11) NOT NULL COMMENT '库存',
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
缓存预热技术
活动开始前将商品信息加载到Redis,使用哈希结构存储:
$redis->hMSet('seckill:goods:1', [
'stock' => 100,
'start' => strtotime('2023-06-01 20:00:00'),
'end' => strtotime('2023-06-01 21:00:00')
]);
原子计数器实现
Redis的DECR命令保证原子性减库存:
$remaining = $redis->eval(
"local stock = tonumber(redis.call('HGET', KEYS[1], 'stock'))
if stock <= 0 then return 0 end
redis.call('HINCRBY', KEYS[1], 'stock', -1)
return stock - 1",
1, 'seckill:goods:1'
);
消息队列削峰
使用RabbitMQ或Kafka处理下单请求,PHP示例:

$channel->queue_declare('seckill_queue', false, true, false, false);
$msg = new AMQPMessage(json_encode(['user_id'=>1, 'goods_id'=>1]));
$channel->basic_publish($msg, '', 'seckill_queue');
分布式锁控制
防止超卖使用Redis分布式锁:
$lockKey = 'seckill:lock:1';
$randomValue = uniqid();
if ($redis->set($lockKey, $randomValue, ['NX', 'EX'=>10])) {
try {
// 处理业务逻辑
} finally {
$redis->eval("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) end",
1, $lockKey, $randomValue);
}
}
库存双重校验
数据库层最终校验,使用乐观锁:
UPDATE `seckill_goods` SET `stock` = `stock` - 1
WHERE `id` = 1 AND `stock` > 0;
限流措施实现
Nginx层限流配置:

limit_req_zone $binary_remote_addr zone=seckill:10m rate=100r/s;
location /seckill {
limit_req zone=seckill burst=200;
}
PHP代码层令牌桶算法:
$rate = 100; // 每秒100个令牌
$capacity = 200;
$lastTime = $redis->get('seckill:last_time');
$currentTokens = min($capacity,
($lastTime ? ($redis->get('seckill:tokens') + ($now - $lastTime)*$rate/1000) : $capacity));
if ($currentTokens < 1) {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
$redis->set('seckill:tokens', $currentTokens - 1);
$redis->set('seckill:last_time', microtime(true));
服务降级方案
当系统压力过大时,返回静态页面:
if ($load > 80) {
readfile('static/seckill_fallback.html');
exit;
}
数据一致性保障
使用定时任务同步Redis和数据库库存:
$goods = $redis->hGetAll('seckill:goods:1');
$db->query("UPDATE seckill_goods SET stock = {$goods['stock']} WHERE id = 1");






