php 抢购实现
抢购系统设计要点
高并发抢购系统的核心在于解决超卖、性能瓶颈和数据一致性问题。PHP实现需结合数据库、缓存和队列技术。
数据库层面优化
使用InnoDB引擎的事务和行级锁确保数据一致性。创建商品表时预留库存字段:
CREATE TABLE `seckill_goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stock` int(11) NOT NULL COMMENT '剩余库存',
`version` int(11) NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
悲观锁实现方式:
$pdo->beginTransaction();
$stmt = $pdo->prepare("SELECT stock FROM seckill_goods WHERE id=? FOR UPDATE");
$stmt->execute([$goods_id]);
$stock = $stmt->fetchColumn();
if($stock > 0){
$pdo->prepare("UPDATE seckill_goods SET stock=stock-1 WHERE id=?")->execute([$goods_id]);
$pdo->commit();
}
乐观锁实现
通过版本号控制并发:
$version = $pdo->query("SELECT version FROM seckill_goods WHERE id=$goods_id")->fetchColumn();
$affected = $pdo->exec("UPDATE seckill_goods SET stock=stock-1, version=version+1
WHERE id=$goods_id AND version=$version");
if($affected > 0){
// 抢购成功
}
Redis原子操作
使用Redis的DECR和Lua脚本保证原子性:

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$script = "
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
";
$result = $redis->eval($script, ["goods_stock:$goods_id"], 1);
消息队列削峰
使用RabbitMQ或Redis List处理瞬时流量:
// 生产者
$redis->lPush('seckill_queue', json_encode(['user_id'=>$user_id, 'goods_id'=>$goods_id]));
// 消费者
while(true){
$data = json_decode($redis->rPop('seckill_queue'), true);
processOrder($data['user_id'], $data['goods_id']);
}
限流措施
使用Redis实现令牌桶限流:
function isAllowed($user_id){
$key = "rate_limit:$user_id";
$redis->multi()
->set($key, 10, ['NX', 'EX'=>60])
->decr($key)
->exec();
return $redis->get($key) >= 0;
}
缓存预热
活动开始前将库存加载到Redis:

$stock = $pdo->query("SELECT stock FROM seckill_goods WHERE id=$goods_id")->fetchColumn();
$redis->set("goods_stock:$goods_id", $stock);
防脚本作弊
增加验证码机制:
// 生成验证码
$code = substr(md5(mt_rand()), 0, 4);
$_SESSION['seckill_code'] = $code;
// 校验验证码
if($_POST['code'] != $_SESSION['seckill_code']){
die('验证码错误');
}
服务层优化
使用Swoole等常驻内存框架提升PHP性能:
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->on('request', function ($request, $response) {
$response->header("Content-Type", "text/plain");
$response->end(handleSeckill());
});
$http->start();
数据一致性最终检查
通过定时任务核对Redis与数据库数据:
$redis_stock = $redis->get("goods_stock:$goods_id");
$db_stock = $pdo->query("SELECT stock FROM seckill_goods WHERE id=$goods_id")->fetchColumn();
if($redis_stock != $db_stock){
// 触发报警并修复数据
}






