为了支持高并发订单处理,并结合 MySQL 进行持久化,可以将 Redis 用于高效的队列管理和临时缓存,而 MySQL 用于最终数据存储。下面是一个结合 Redis 和 MySQL 的完整示例。
MySQL 表结构
首先,需要定义 MySQL 表结构来存储订单和库存信息。
CREATE TABLE `orders` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`user_id` BIGINT UNSIGNED NOT NULL,
`product_id` BIGINT UNSIGNED NOT NULL,
`quantity` INT NOT NULL,
`status` VARCHAR(50) DEFAULT 'pending',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `products` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`stock` INT NOT NULL,
`price` DECIMAL(10, 2) NOT NULL
);
PHP 实现
class OrderProcessing
{
private $redis;
private $pdo;
private $queueName;
public function __construct($redis, $pdo, $queueName = 'order_queue')
{
$this->redis = $redis;
$this->pdo = $pdo;
$this->queueName = $queueName;
}
/**
* 提交订单
*/
public function submitOrder($userId, $productId, $quantity)
{
// 先检查库存
$stock = $this->redis->get("product_stock_$productId");
if ($stock === false) {
// 如果 Redis 中没有缓存库存,从数据库中读取并缓存
$stmt = $this->pdo->prepare("SELECT stock FROM products WHERE id = :id");
$stmt->execute(['id' => $productId]);
$stock = $stmt->fetchColumn();
$this->redis->set("product_stock_$productId", $stock);
}
if ($stock >= $quantity) {
// 将订单数据推送到 Redis 队列中
$orderData = [
'user_id' => $userId,
'product_id' => $productId,
'quantity' => $quantity
];
$this->redis->lpush($this->queueName, json_encode($orderData));
// 减少 Redis 中的库存
$this->redis->decrBy("product_stock_$productId", $quantity);
return "Order submitted successfully.";
} else {
return "Insufficient stock.";
}
}
/**
* 处理订单(消费者)
*/
public function processOrders()
{
while (true) {
$orderData = $this->redis->rpop($this->queueName);
if ($orderData) {
$order = json_decode($orderData, true);
$this->processOrder($order);
} else {
usleep(500000); // 休眠 500ms
}
}
}
/**
* 实际处理订单的逻辑
*/
private function processOrder($order)
{
try {
$this->pdo->beginTransaction();
// 插入订单记录
$stmt = $this->pdo->prepare("INSERT INTO orders (user_id, product_id, quantity) VALUES (:user_id, :product_id, :quantity)");
$stmt->execute([
'user_id' => $order['user_id'],
'product_id' => $order['product_id'],
'quantity' => $order['quantity']
]);
// 更新数据库中的库存
$stmt = $this->pdo->prepare("UPDATE products SET stock = stock - :quantity WHERE id = :id");
$stmt->execute([
'quantity' => $order['quantity'],
'id' => $order['product_id']
]);
$this->pdo->commit();
} catch (Exception $e) {
$this->pdo->rollBack();
// 将订单重新放回队列或记录日志
$this->redis->lpush($this->queueName, json_encode($order));
echo "Failed to process order: " . $e->getMessage() . PHP_EOL;
}
}
}
代码详解
-
提交订单 (
submitOrder
):- 首先检查 Redis 中是否有该产品的库存。如果没有,则从数据库中读取并缓存到 Redis 中。
- 如果库存充足,则将订单数据推送到 Redis 队列,并在 Redis 中扣减库存。
-
处理订单 (
processOrders
):- 订单处理由消费者进程执行,从 Redis 队列中取出订单数据,并将订单插入数据库,同时更新数据库中的库存。
- 为了确保数据一致性,使用 MySQL 的事务处理订单和库存的更新。
-
实际处理订单的逻辑 (
processOrder
):- 该方法执行插入订单记录和更新库存的具体操作,并确保在出现错误时能够回滚事务并重新处理订单。
优化建议
-
多消费者进程:
- 可以启动多个消费者进程来并发处理订单,以提高订单处理速度。
-
Redis 集群:
- 如果并发量非常大,建议使用 Redis 集群来分散负载。
-
异步处理:
- 进一步优化可以将某些任务(如发送确认邮件)放入异步队列中处理,以减轻主订单处理的压力。
此方案利用 Redis 的原子操作和 MySQL 的持久化存储,能够有效支持每分钟数千笔订单的高并发处理。