php 抢购实现思路
数据库设计
使用InnoDB引擎确保事务支持,创建商品表包含库存字段(如stock),订单表记录抢购成功数据。关键字段需添加索引,如商品ID和用户ID组合索引。
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stock` int(11) NOT NULL COMMENT '库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_product_user` (`product_id`,`user_id`)
) ENGINE=InnoDB;
悲观锁实现
在事务内使用SELECT FOR UPDATE锁定商品记录,确保库存检查与扣减的原子性。需注意事务粒度控制,避免长时间锁表。
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare('SELECT stock FROM product WHERE id = ? FOR UPDATE');
$stmt->execute([$productId]);
$stock = $stmt->fetchColumn();
if ($stock > 0) {
$pdo->prepare('UPDATE product SET stock = stock - 1 WHERE id = ?')->execute([$productId]);
$pdo->prepare('INSERT INTO order (product_id, user_id) VALUES (?, ?)')->execute([$productId, $userId]);
}
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
}
乐观锁实现
通过版本号或时间戳字段实现无锁并发控制。更新时校验版本号,冲突时自动重试或失败。适合并发量中等场景。

$version = $pdo->query("SELECT version FROM product WHERE id = $productId")->fetchColumn();
$affected = $pdo->exec("UPDATE product SET stock = stock - 1, version = version + 1
WHERE id = $productId AND version = $version");
if ($affected === 0) {
throw new Exception('抢购失败');
}
缓存预减库存
采用Redis原子操作预减库存,Lua脚本保证操作原子性。实际库存不足时快速返回,减轻数据库压力。
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$lua = "
local stock = tonumber(redis.call('get', KEYS[1]))
if stock > 0 then
redis.call('decr', KEYS[1])
return 1
end
return 0
";
$res = $redis->eval($lua, ['product_stock_' . $productId], 1);
if ($res === 0) {
die('已售罄');
}
队列削峰
使用RabbitMQ或Kafka将请求异步化,消费者顺序处理订单。结合令牌桶算法控制处理速率,实现流量整形。

$channel->queue_declare('seckill', false, true, false, false);
$channel->basic_publish(
new AMQPMessage(json_encode(['product_id' => $productId, 'user_id' => $userId]),
'',
'seckill'
);
限流措施
Nginx层实现IP限流,PHP应用层使用Redis计数器限制用户频率。滑动窗口算法统计单位时间请求量。
$key = 'user_limit_' . $userId;
$count = $redis->incr($key);
if ($count === 1) {
$redis->expire($key, 10);
}
if ($count > 5) {
die('操作过于频繁');
}
数据一致性
通过定时任务核对Redis与数据库库存差异,引入分布式锁处理超卖。采用本地消息表实现最终一致性。
// 核对脚本示例
$dbStock = $pdo->query("SELECT stock FROM product WHERE id = 1")->fetchColumn();
$redisStock = $redis->get('product_stock_1');
if ($dbStock != $redisStock) {
// 触发补偿逻辑
}
前端优化
静态资源CDN加速,按钮提交后禁用防止重复点击。倒计时结束前预加载接口,减少网络延迟影响。
document.getElementById('btn').addEventListener('click', function() {
this.disabled = true;
fetch('/seckill', {method: 'POST'});
});






