php实现抢购
PHP实现抢购功能
抢购功能需要处理高并发请求,确保数据的准确性和系统的稳定性。以下是实现抢购功能的关键步骤:
数据库设计 创建商品表和订单表,商品表需包含库存字段,订单表记录抢购成功的用户信息。使用事务和锁机制保证数据一致性。
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`stock` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
悲观锁实现 在查询商品库存时使用SELECT FOR UPDATE锁定记录,防止其他进程同时修改。
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("SELECT stock FROM product WHERE id = ? FOR UPDATE");
$stmt->execute([$productId]);
$product = $stmt->fetch();
if ($product['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();
echo "抢购成功";
} else {
$pdo->rollBack();
echo "库存不足";
}
} catch (Exception $e) {
$pdo->rollBack();
echo "系统繁忙";
}
Redis缓存和队列 使用Redis原子操作减轻数据库压力,先将请求放入队列再异步处理。
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 检查库存
$stock = $redis->get("product_stock_{$productId}");
if ($stock <= 0) {
echo "已售罄";
exit;
}
// 原子减库存
$remain = $redis->decr("product_stock_{$productId}");
if ($remain >= 0) {
// 将订单信息放入队列
$orderData = json_encode(['product_id' => $productId, 'user_id' => $userId]);
$redis->lPush('order_queue', $orderData);
echo "抢购成功";
} else {
$redis->incr("product_stock_{$productId}"); // 恢复库存
echo "库存不足";
}
限流措施 使用Redis实现计数器限流,防止系统过载。
$key = "user_request_{$userId}";
$limit = 5; // 每秒限制5次请求
if ($redis->incr($key) > $limit) {
echo "请求过于频繁";
exit;
}
$redis->expire($key, 1); // 1秒过期
静态化页面 将商品详情页生成静态HTML,减少服务器动态请求压力。使用Nginx直接返回静态文件。
分布式锁 在集群环境下使用Redis实现分布式锁,确保同一时间只有一个进程处理库存扣减。
$lockKey = "product_lock_{$productId}";
$randomValue = uniqid();
$locked = $redis->set($lockKey, $randomValue, ['NX', 'EX' => 10]);
if ($locked) {
try {
// 处理抢购逻辑
} finally {
// 确保释放自己的锁
if ($redis->get($lockKey) == $randomValue) {
$redis->del($lockKey);
}
}
} else {
echo "系统繁忙";
}
异步处理 使用消息队列将订单处理与抢购请求分离,提高响应速度。RabbitMQ或Kafka都是可选方案。
库存预热 活动开始前将库存加载到Redis,避免直接查询数据库。定时同步Redis和数据库的库存数据。
验证码机制 在抢购前增加图形验证码或短信验证码,防止机器人刷单。







