
第一部分:什么是PHP+MySQL+Redis队列的秒杀系统?
1. 生活中的例子
- 想象一下,你在游乐场排队玩一个超级受欢迎的游戏。游戏只有一个位置,但有100个小朋友都想玩。工作人员会:
- 先让每个小朋友拿一张号码牌(相当于加入Redis队列)。
- 然后按顺序叫号,确保每个人都能公平地玩到游戏。
- 最后记录谁玩了游戏(相当于保存订单信息到MySQL)。
- 在编程里:
秒杀系统就是类似的概念。它需要处理大量用户同时抢购有限商品的场景,并确保:
- 商品不会超卖。
- 每个用户只能购买一次。
- 使用队列来分担高并发压力。
第二部分:秒杀系统的全流程包含哪些部分?
1. 主要组成部分
- Redis缓存层
- 使用Redis存储库存信息和队列,减少对MySQL数据库的压力。
- 分布式锁
- 使用Redis的分布式锁(
SETNX
)确保同一时间只有一个用户能操作库存。
- Redis队列
- 使用Redis队列(如
List
或Stream
)将用户的请求排队,避免直接访问数据库。
- MySQL持久化层
- 将用户的订单信息保存到MySQL中,确保数据持久化。
- 消费者处理
- 使用后台进程(消费者)从Redis队列中取出请求并处理。
第三部分:背后到底做了哪些事情?
1. 秒杀系统的工作流程
- 用户请求入队:用户的秒杀请求被放入Redis队列。
- 消费者处理请求:后台消费者从队列中取出请求,检查库存并生成订单。
- 加锁:使用Redis的分布式锁锁定库存,防止多个消费者同时修改。
- 扣减库存:在确认库存充足后,原子性地扣减库存。
- 生成订单:将订单信息写入MySQL数据库。
- 释放锁:完成操作后,释放分布式锁。
第四部分:示例代码与详细讲解
1. 示例代码:基于PHP+MySQL+Redis队列实现秒杀系统
前端代码:用户提交秒杀请求
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$itemId = 1;
$userId = 123;
$queueKey = "item_queue:$itemId";
$redis->lPush($queueKey, json_encode(['user_id' => $userId, 'item_id' => $itemId]));
echo "秒杀请求已提交,请等待处理。\n";
消费者代码:处理秒杀请求
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$itemId = 1;
$stockKey = "item_stock:$itemId";
$lockKey = "item_lock:$itemId";
$queueKey = "item_queue:$itemId";
while (true) {
$request = $redis->rPop($queueKey);
if (!$request) {
sleep(1);
continue;
}
$data = json_decode($request, true);
$userId = $data['user_id'];
$clientId = uniqid();
try {
$stock = $redis->get($stockKey);
if ($stock <= 0) {
echo "库存不足,秒杀失败(用户ID:$userId)。\n";
continue;
}
$isLocked = $redis->set($lockKey, $clientId, ['nx', 'ex' => 5]);
if (!$isLocked) {
echo "系统繁忙,稍后再试(用户ID:$userId)。\n";
continue;
}
try {
$stock = $redis->get($stockKey);
if ($stock <= 0) {
echo "库存不足,秒杀失败(用户ID:$userId)。\n";
continue;
}
$newStock = $redis->decr($stockKey);
$stmt = $pdo->prepare("INSERT INTO orders (user_id, item_id) VALUES (:user_id, :item_id)");
$stmt->execute(['user_id' => $userId, 'item_id' => $itemId]);
echo "秒杀成功!剩余库存:" . $newStock . "(用户ID:$userId)。\n";
} finally {
if ($redis->get($lockKey) === $clientId) {
$redis->del($lockKey);
echo "锁已释放(用户ID:$userId)。\n";
}
}
} catch (Exception $e) {
echo "发生错误:" . $e->getMessage() . "\n";
}
}
第五部分:使用场景
1. 高并发秒杀活动
- 当大量用户同时抢购有限的商品时,确保库存不会超卖。
2. 限时优惠
- 在促销活动中,确保每个用户只能购买一次或限定数量。
3. 高并发系统
- 在电商、票务等高并发场景中,保证系统的稳定性和数据一致性。
第六部分:底层原理是什么?
1. Redis的原子操作
- Redis的
DECR
命令是原子性的,意味着“检查并扣减库存”这两个操作是一次性完成的,不会被其他程序打断。
2. 分布式锁的安全性
- 使用唯一标识符(
clientId
)确保只有持有锁的程序才能释放锁,避免误删别人的锁。
3. Redis队列的作用
- Redis队列将用户的请求排队,避免直接访问MySQL数据库,减轻数据库压力。
4. 异步处理
- 消费者从Redis队列中取出请求并异步处理,提升系统吞吐量。
第七部分:总结
1. 秒杀系统的核心
- 库存保护:通过分布式锁和原子操作,确保库存不会超卖。
- 性能优化:通过Redis缓存和队列,提升系统的并发处理能力。
- 数据持久化:通过MySQL保存订单信息,确保数据安全。
2. 生活中的类比
- 秒杀系统就像游乐场里的排队游戏,工作人员先发号码牌(Redis队列),然后按顺序叫号(消费者处理),最后记录谁玩了游戏(MySQL持久化)。