为了支持高并发订单处理,并结合 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 的持久化存储,能够有效支持每分钟数千笔订单的高并发处理。