[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-changjingmianshitiruhebimianyonghuzhongfuxiadanduocixiadanweizhifuzhanyongkucun":3},{"id":4,"title":5,"slug":6,"summary":7,"content":8,"contentHtml":9,"wordCount":10,"readingTime":11,"categoryId":12,"tags":13,"coverImage":13,"thumbnail":13,"status":14,"isTop":15,"isRecommended":15,"allowComments":15,"password":13,"viewCount":16,"likeCount":15,"commentCount":15,"seoTitle":5,"seoKeywords":17,"seoDescription":18,"source":13,"sourceUrl":13,"publishTime":19},"2005630101352411138","场景面试题：如何避免用户重复下单（多次下单未支付，占用库存）","changjingmianshitiruhebimianyonghuzhongfuxiadanduocixiadanweizhifuzhanyongkucun","# Spring Boot 如何避免用户重复下单（多次下单未支付，占用库存）？—— 面试题详解 在高并发电商系统中，一个常见的问题是：**用户短时间内多次点击“下单”按钮，生成多个未支付订单，导致库存被无效占用，影响其他用户购买**。这不仅浪费系统资源，还可能引发超卖或库存不足的问题。 本文将围绕 **Spring Boot 技术栈**，从原理、方案设计到代码实现，详细讲解如何有效防止用户重复下单，并形成一套可落地的解决方案。 --- ## 一、问题场景还原（面试题背景） > **面试官提问**： > “在我们的电商平台中，经常有用户在未支付的情况下多次点击下单按钮，造成多个待支付订单和库存锁定。请问你如何在 Spring Boot 项目中解决这个问题？” 这是一个典型的 **幂等性 + 库存管理 + 分布式锁** 综合问题。 --- ## 二、核心思路分析 要解决重复下单问题，需从以下维...","# Spring Boot 如何避免用户重复下单（多次下单未支付，占用库存）？—— 面试题详解\n\n在高并发电商系统中，一个常见的问题是：**用户短时间内多次点击“下单”按钮，生成多个未支付订单，导致库存被无效占用，影响其他用户购买**。这不仅浪费系统资源，还可能引发超卖或库存不足的问题。\n\n本文将围绕 **Spring Boot 技术栈**，从原理、方案设计到代码实现，详细讲解如何有效防止用户重复下单，并形成一套可落地的解决方案。\n\n---\n\n## 一、问题场景还原（面试题背景）\n\n> **面试官提问**：  \n> “在我们的电商平台中，经常有用户在未支付的情况下多次点击下单按钮，造成多个待支付订单和库存锁定。请问你如何在 Spring Boot 项目中解决这个问题？”\n\n这是一个典型的 **幂等性 + 库存管理 + 分布式锁** 综合问题。\n\n---\n\n## 二、核心思路分析\n\n要解决重复下单问题，需从以下维度入手：\n\n1. **前端防重**（基础但不可靠）\n2. **后端接口幂等性控制**\n3. **库存预占与释放机制**\n4. **分布式锁防止并发创建订单**\n5. **定时任务清理未支付订单**\n\n> 注意：仅靠前端防重是不够的，必须在后端做兜底。\n\n---\n\n## 三、Spring Boot 实现方案详解\n\n### 方案一：基于 Redis 的幂等令牌（Token）机制 ✅（推荐）\n\n#### 原理：\n- 用户进入下单页面时，后端生成一个唯一 `orderToken` 并存入 Redis（带过期时间，如 5 分钟）。\n- 提交订单时，携带该 `orderToken`。\n- 后端校验 Token 是否存在且有效，若存在则消费（删除），并继续创建订单；否则拒绝请求。\n\n#### 优点：\n- 简单高效，天然支持幂等。\n- 可防止用户刷新页面重复提交。\n\n#### 代码实现：\n\n```java\n\u002F\u002F 1. 生成下单 Token\n@RestController\npublic class OrderController {\n\n    @Autowired\n    private RedisTemplate\u003CString, String> redisTemplate;\n\n    @GetMapping(\"\u002FcreateOrderToken\")\n    public String createOrderToken(@RequestParam Long userId) {\n        String token = UUID.randomUUID().toString();\n        String key = \"ORDER_TOKEN:\" + userId;\n        redisTemplate.opsForValue().set(key, token, Duration.ofMinutes(5));\n        return token;\n    }\n\n    \u002F\u002F 2. 提交订单\n    @PostMapping(\"\u002FsubmitOrder\")\n    public ResponseEntity\u003CString> submitOrder(@RequestBody OrderRequest request) {\n        String tokenKey = \"ORDER_TOKEN:\" + request.getUserId();\n        String storedToken = redisTemplate.opsForValue().get(tokenKey);\n\n        if (storedToken == null || !storedToken.equals(request.getOrderToken())) {\n            return ResponseEntity.badRequest().body(\"重复提交或 Token 无效\");\n        }\n\n        \u002F\u002F 尝试删除 Token（原子操作）\n        Boolean deleted = redisTemplate.delete(tokenKey);\n        if (!deleted) {\n            return ResponseEntity.badRequest().body(\"请勿重复提交\");\n        }\n\n        \u002F\u002F 执行创建订单逻辑（需加分布式锁，见下文）\n        createRealOrder(request);\n        return ResponseEntity.ok(\"下单成功\");\n    }\n}\n```\n\n---\n\n### 方案二：基于用户 + 商品维度的分布式锁\n\n即使有 Token，仍需防止极端情况下的并发请求（如用户开多个 tab 同时下单）。\n\n#### 使用 Redisson 分布式锁：\n\n```java\n@Autowired\nprivate RedissonClient redisson;\n\nprivate void createRealOrder(OrderRequest request) {\n    String lockKey = \"ORDER_LOCK:\" + request.getUserId() + \":\" + request.getProductId();\n\n    RLock lock = redisson.getLock(lockKey);\n    try {\n        boolean acquired = lock.tryLock(1, 10, TimeUnit.SECONDS);\n        if (!acquired) {\n            throw new RuntimeException(\"系统繁忙，请稍后再试\");\n        }\n\n        \u002F\u002F 再次检查是否已有未支付订单（双重校验）\n        if (hasUnpaidOrder(request.getUserId(), request.getProductId())) {\n            throw new RuntimeException(\"您有未支付订单，请先完成支付\");\n        }\n\n        \u002F\u002F 扣减预占库存 & 创建订单\n        inventoryService.reserveStock(request.getProductId(), request.getQuantity());\n        orderService.createOrder(request);\n\n    } finally {\n        lock.unlock();\n    }\n}\n```\n\n---\n\n### 方案三：库存预占 + 定时释放\n\n- 下单时 **预占库存**（非真实扣减），设置过期时间（如 15 分钟）。\n- 若用户未支付，通过 **定时任务** 或 **延迟队列** 自动释放库存。\n\n#### 示例（使用 Redis 过期键 + 监听）：\n\n```java\n\u002F\u002F 预占库存\npublic void reserveStock(Long productId, Integer quantity) {\n    String stockKey = \"STOCK_RESERVED:\" + productId;\n    \u002F\u002F 使用 Lua 脚本保证原子性\n    redisTemplate.execute(reserveStockScript, \n        Collections.singletonList(stockKey), \n        quantity.toString(), \n        Duration.ofMinutes(15).getSeconds()\n    );\n}\n\n\u002F\u002F Lua 脚本示例（伪代码）\n\u002F\u002F eval \"if redis.call('GET', KEYS[1]) then ... end\"\n```\n\n> 更高级方案：使用 RabbitMQ 延迟队列 或 Redis Streams + 消费者监听过期事件。\n\n---\n\n### 方案四：数据库层面兜底（最终防线）\n\n在订单表中添加唯一约束或状态检查：\n\n```sql\n-- 订单表增加联合索引（用户ID + 商品ID + 状态）\nCREATE UNIQUE INDEX idx_user_product_unpaid \nON orders(user_id, product_id) \nWHERE status = 'UNPAID';\n```\n\n> 注意：MySQL 需 8.0+ 支持函数索引，否则可建普通索引 + 应用层查询判断。\n\nJava 层校验：\n\n```java\npublic boolean hasUnpaidOrder(Long userId, Long productId) {\n    return orderRepository.existsByUserIdAndProductIdAndStatus(\n        userId, productId, OrderStatus.UNPAID\n    );\n}\n```\n\n---\n\n## 四、完整流程图（逻辑顺序）\n\n```\n用户 → 获取 orderToken → 提交订单（带 Token）\n       ↓\n后端校验 Token（Redis）→ 失败？→ 返回“重复提交”\n       ↓ 成功\n获取分布式锁（用户+商品维度）\n       ↓\n检查是否存在未支付订单 → 是？→ 返回“请先支付”\n       ↓ 否\n预占库存（Redis + 过期）\n       ↓\n创建订单（DB）\n       ↓\n返回成功\n       ↓\n【后台】15分钟后未支付 → 释放库存 + 关闭订单\n```\n\n---\n\n## 五、面试回答模板（精简版）\n\n> “针对用户重复下单问题，我会采用 **前后端结合 + 多层防护** 的策略：\n>\n> 1. **前端**：点击后禁用按钮，防止快速连点；\n> 2. **后端**：\n>    - 引入 **下单 Token 机制**，利用 Redis 实现接口幂等；\n>    - 使用 **Redisson 分布式锁**，按用户+商品维度加锁，防止并发创建；\n>    - 下单前 **查询是否存在未支付订单**，若有则拒绝；\n>    - 采用 **库存预占 + 定时释放** 机制，避免库存长期被占用；\n> 3. **兜底**：数据库加唯一索引或应用层校验，确保数据一致性。\n>\n> 此外，配合 **延迟队列** 或 **定时任务** 自动清理超时未支付订单，释放库存。”\n\n---\n\n## 六、扩展思考\n\n- 如何处理 Token 被恶意刷取？\n  → 限流（如 Guava RateLimiter 或 Sentinel）\n- 如何保证库存预占的原子性？\n  → Redis Lua 脚本 or 分布式事务（Seata，但成本高）\n- 高并发下 Redis 成为瓶颈？\n  → 分片、本地缓存 + 二级缓存策略\n\n---\n\n## 总结\n\n防止重复下单不是单一技术点，而是 **系统性工程**。在 Spring Boot 项目中，应结合 **Redis 幂等令牌、分布式锁、库存预占、定时清理** 等手段，构建多层次防御体系。既保障用户体验，又确保系统稳定性和数据一致性。\n\n> 💡 **关键点**：**幂等性是核心，分布式锁是保障，库存释放是闭环。**\n\n--- \n\n希望这篇文章能帮助你在面试中从容应对“重复下单”类问题！","\u003Ch1 data-line=\"0\" id=\"Spring Boot 如何避免用户重复下单（多次下单未支付，占用库存）？—— 面试题详解\">Spring Boot 如何避免用户重复下单（多次下单未支付，占用库存）？—— 面试题详解\u003C\u002Fh1>\n\u003Cp data-line=\"2\">在高并发电商系统中，一个常见的问题是：\u003Cstrong>用户短时间内多次点击“下单”按钮，生成多个未支付订单，导致库存被无效占用，影响其他用户购买\u003C\u002Fstrong>。这不仅浪费系统资源，还可能引发超卖或库存不足的问题。\u003C\u002Fp>\n\u003Cp data-line=\"4\">本文将围绕 \u003Cstrong>Spring Boot 技术栈\u003C\u002Fstrong>，从原理、方案设计到代码实现，详细讲解如何有效防止用户重复下单，并形成一套可落地的解决方案。\u003C\u002Fp>\n\u003Chr data-line=\"6\">\n\u003Ch2 data-line=\"8\" id=\"一、问题场景还原（面试题背景）\">一、问题场景还原（面试题背景）\u003C\u002Fh2>\n\u003Cblockquote data-line=\"10\">\n\u003Cp data-line=\"10\">\u003Cstrong>面试官提问\u003C\u002Fstrong>：\u003Cbr>\n“在我们的电商平台中，经常有用户在未支付的情况下多次点击下单按钮，造成多个待支付订单和库存锁定。请问你如何在 Spring Boot 项目中解决这个问题？”\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp data-line=\"13\">这是一个典型的 \u003Cstrong>幂等性 + 库存管理 + 分布式锁\u003C\u002Fstrong> 综合问题。\u003C\u002Fp>\n\u003Chr data-line=\"15\">\n\u003Ch2 data-line=\"17\" id=\"二、核心思路分析\">二、核心思路分析\u003C\u002Fh2>\n\u003Cp data-line=\"19\">要解决重复下单问题，需从以下维度入手：\u003C\u002Fp>\n\u003Col data-line=\"21\">\n\u003Cli data-line=\"21\">\u003Cstrong>前端防重\u003C\u002Fstrong>（基础但不可靠）\u003C\u002Fli>\n\u003Cli data-line=\"22\">\u003Cstrong>后端接口幂等性控制\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli data-line=\"23\">\u003Cstrong>库存预占与释放机制\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli data-line=\"24\">\u003Cstrong>分布式锁防止并发创建订单\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli data-line=\"25\">\u003Cstrong>定时任务清理未支付订单\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cblockquote data-line=\"27\">\n\u003Cp data-line=\"27\">注意：仅靠前端防重是不够的，必须在后端做兜底。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr data-line=\"29\">\n\u003Ch2 data-line=\"31\" id=\"三、Spring Boot 实现方案详解\">三、Spring Boot 实现方案详解\u003C\u002Fh2>\n\u003Ch3 data-line=\"33\" id=\"方案一：基于 Redis 的幂等令牌（Token）机制 ✅（推荐）\">方案一：基于 Redis 的幂等令牌（Token）机制 ✅（推荐）\u003C\u002Fh3>\n\u003Ch4 data-line=\"35\" id=\"原理：\">原理：\u003C\u002Fh4>\n\u003Cul data-line=\"36\">\n\u003Cli data-line=\"36\">用户进入下单页面时，后端生成一个唯一 \u003Ccode>orderToken\u003C\u002Fcode> 并存入 Redis（带过期时间，如 5 分钟）。\u003C\u002Fli>\n\u003Cli data-line=\"37\">提交订单时，携带该 \u003Ccode>orderToken\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli data-line=\"38\">后端校验 Token 是否存在且有效，若存在则消费（删除），并继续创建订单；否则拒绝请求。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch4 data-line=\"40\" id=\"优点：\">优点：\u003C\u002Fh4>\n\u003Cul data-line=\"41\">\n\u003Cli data-line=\"41\">简单高效，天然支持幂等。\u003C\u002Fli>\n\u003Cli data-line=\"42\">可防止用户刷新页面重复提交。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch4 data-line=\"44\" id=\"代码实现：\">代码实现：\u003C\u002Fh4>\n\n        \u003Cdetails  data-line=\"46\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F 1. 生成下单 Token\u003C\u002Fspan>\n\u003Cspan class=\"hljs-meta\">@RestController\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">OrderController\u003C\u002Fspan> {\n\n    \u003Cspan class=\"hljs-meta\">@Autowired\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> RedisTemplate&lt;String, String&gt; redisTemplate;\n\n    \u003Cspan class=\"hljs-meta\">@GetMapping(&quot;\u002FcreateOrderToken&quot;)\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> String \u003Cspan class=\"hljs-title function_\">createOrderToken\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(\u003Cspan class=\"hljs-meta\">@RequestParam\u003C\u002Fspan> Long userId)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">token\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> UUID.randomUUID().toString();\n        \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">key\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;ORDER_TOKEN:&quot;\u003C\u002Fspan> + userId;\n        redisTemplate.opsForValue().set(key, token, Duration.ofMinutes(\u003Cspan class=\"hljs-number\">5\u003C\u002Fspan>));\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> token;\n    }\n\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 2. 提交订单\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-meta\">@PostMapping(&quot;\u002FsubmitOrder&quot;)\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> ResponseEntity&lt;String&gt; \u003Cspan class=\"hljs-title function_\">submitOrder\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(\u003Cspan class=\"hljs-meta\">@RequestBody\u003C\u002Fspan> OrderRequest request)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">tokenKey\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;ORDER_TOKEN:&quot;\u003C\u002Fspan> + request.getUserId();\n        \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">storedToken\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> redisTemplate.opsForValue().get(tokenKey);\n\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (storedToken == \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan> || !storedToken.equals(request.getOrderToken())) {\n            \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ResponseEntity.badRequest().body(\u003Cspan class=\"hljs-string\">&quot;重复提交或 Token 无效&quot;\u003C\u002Fspan>);\n        }\n\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 尝试删除 Token（原子操作）\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-type\">Boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">deleted\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> redisTemplate.delete(tokenKey);\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!deleted) {\n            \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ResponseEntity.badRequest().body(\u003Cspan class=\"hljs-string\">&quot;请勿重复提交&quot;\u003C\u002Fspan>);\n        }\n\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 执行创建订单逻辑（需加分布式锁，见下文）\u003C\u002Fspan>\n        createRealOrder(request);\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ResponseEntity.ok(\u003Cspan class=\"hljs-string\">&quot;下单成功&quot;\u003C\u002Fspan>);\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Chr data-line=\"85\">\n\u003Ch3 data-line=\"87\" id=\"方案二：基于用户 + 商品维度的分布式锁\">方案二：基于用户 + 商品维度的分布式锁\u003C\u002Fh3>\n\u003Cp data-line=\"89\">即使有 Token，仍需防止极端情况下的并发请求（如用户开多个 tab 同时下单）。\u003C\u002Fp>\n\u003Ch4 data-line=\"91\" id=\"使用 Redisson 分布式锁：\">使用 Redisson 分布式锁：\u003C\u002Fh4>\n\n        \u003Cdetails  data-line=\"93\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Autowired\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> RedissonClient redisson;\n\n\u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">createRealOrder\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(OrderRequest request)\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">lockKey\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;ORDER_LOCK:&quot;\u003C\u002Fspan> + request.getUserId() + \u003Cspan class=\"hljs-string\">&quot;:&quot;\u003C\u002Fspan> + request.getProductId();\n\n    \u003Cspan class=\"hljs-type\">RLock\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">lock\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> redisson.getLock(lockKey);\n    \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-type\">boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">acquired\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> lock.tryLock(\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">10\u003C\u002Fspan>, TimeUnit.SECONDS);\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!acquired) {\n            \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;系统繁忙，请稍后再试&quot;\u003C\u002Fspan>);\n        }\n\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 再次检查是否已有未支付订单（双重校验）\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (hasUnpaidOrder(request.getUserId(), request.getProductId())) {\n            \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;您有未支付订单，请先完成支付&quot;\u003C\u002Fspan>);\n        }\n\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 扣减预占库存 &amp; 创建订单\u003C\u002Fspan>\n        inventoryService.reserveStock(request.getProductId(), request.getQuantity());\n        orderService.createOrder(request);\n\n    } \u003Cspan class=\"hljs-keyword\">finally\u003C\u002Fspan> {\n        lock.unlock();\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Chr data-line=\"122\">\n\u003Ch3 data-line=\"124\" id=\"方案三：库存预占 + 定时释放\">方案三：库存预占 + 定时释放\u003C\u002Fh3>\n\u003Cul data-line=\"126\">\n\u003Cli data-line=\"126\">下单时 \u003Cstrong>预占库存\u003C\u002Fstrong>（非真实扣减），设置过期时间（如 15 分钟）。\u003C\u002Fli>\n\u003Cli data-line=\"127\">若用户未支付，通过 \u003Cstrong>定时任务\u003C\u002Fstrong> 或 \u003Cstrong>延迟队列\u003C\u002Fstrong> 自动释放库存。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch4 data-line=\"129\" id=\"示例（使用 Redis 过期键 + 监听）：\">示例（使用 Redis 过期键 + 监听）：\u003C\u002Fh4>\n\n        \u003Cdetails  data-line=\"131\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F 预占库存\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">reserveStock\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(Long productId, Integer quantity)\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">stockKey\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;STOCK_RESERVED:&quot;\u003C\u002Fspan> + productId;\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 使用 Lua 脚本保证原子性\u003C\u002Fspan>\n    redisTemplate.execute(reserveStockScript, \n        Collections.singletonList(stockKey), \n        quantity.toString(), \n        Duration.ofMinutes(\u003Cspan class=\"hljs-number\">15\u003C\u002Fspan>).getSeconds()\n    );\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F Lua 脚本示例（伪代码）\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F eval &quot;if redis.call(&#x27;GET&#x27;, KEYS[1]) then ... end&quot;\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cblockquote data-line=\"147\">\n\u003Cp data-line=\"147\">更高级方案：使用 RabbitMQ 延迟队列 或 Redis Streams + 消费者监听过期事件。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr data-line=\"149\">\n\u003Ch3 data-line=\"151\" id=\"方案四：数据库层面兜底（最终防线）\">方案四：数据库层面兜底（最终防线）\u003C\u002Fh3>\n\u003Cp data-line=\"153\">在订单表中添加唯一约束或状态检查：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"155\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">sql\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-sql\" language=sql>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">-- 订单表增加联合索引（用户ID + 商品ID + 状态）\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">CREATE\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">UNIQUE\u003C\u002Fspan> INDEX idx_user_product_unpaid \n\u003Cspan class=\"hljs-keyword\">ON\u003C\u002Fspan> orders(user_id, product_id) \n\u003Cspan class=\"hljs-keyword\">WHERE\u003C\u002Fspan> status \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;UNPAID&#x27;\u003C\u002Fspan>;\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cblockquote data-line=\"162\">\n\u003Cp data-line=\"162\">注意：MySQL 需 8.0+ 支持函数索引，否则可建普通索引 + 应用层查询判断。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp data-line=\"164\">Java 层校验：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"166\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">hasUnpaidOrder\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(Long userId, Long productId)\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> orderRepository.existsByUserIdAndProductIdAndStatus(\n        userId, productId, OrderStatus.UNPAID\n    );\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Chr data-line=\"174\">\n\u003Ch2 data-line=\"176\" id=\"四、完整流程图（逻辑顺序）\">四、完整流程图（逻辑顺序）\u003C\u002Fh2>\n\n        \u003Cdetails  data-line=\"178\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">用户 → 获取 orderToken → 提交订单（带 Token）\n\u003Cspan class=\"hljs-code\">       ↓\n后端校验 Token（Redis）→ 失败？→ 返回“重复提交”\n       ↓ 成功\n获取分布式锁（用户+商品维度）\n       ↓\n检查是否存在未支付订单 → 是？→ 返回“请先支付”\n       ↓ 否\n预占库存（Redis + 过期）\n       ↓\n创建订单（DB）\n       ↓\n返回成功\n       ↓\n【后台】15分钟后未支付 → 释放库存 + 关闭订单\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Chr data-line=\"196\">\n\u003Ch2 data-line=\"198\" id=\"五、面试回答模板（精简版）\">五、面试回答模板（精简版）\u003C\u002Fh2>\n\u003Cblockquote data-line=\"200\">\n\u003Cp data-line=\"200\">“针对用户重复下单问题，我会采用 \u003Cstrong>前后端结合 + 多层防护\u003C\u002Fstrong> 的策略：\u003C\u002Fp>\n\u003Col data-line=\"202\">\n\u003Cli data-line=\"202\">\u003Cstrong>前端\u003C\u002Fstrong>：点击后禁用按钮，防止快速连点；\u003C\u002Fli>\n\u003Cli data-line=\"203\">\u003Cstrong>后端\u003C\u002Fstrong>：\n\u003Cul data-line=\"204\">\n\u003Cli data-line=\"204\">引入 \u003Cstrong>下单 Token 机制\u003C\u002Fstrong>，利用 Redis 实现接口幂等；\u003C\u002Fli>\n\u003Cli data-line=\"205\">使用 \u003Cstrong>Redisson 分布式锁\u003C\u002Fstrong>，按用户+商品维度加锁，防止并发创建；\u003C\u002Fli>\n\u003Cli data-line=\"206\">下单前 \u003Cstrong>查询是否存在未支付订单\u003C\u002Fstrong>，若有则拒绝；\u003C\u002Fli>\n\u003Cli data-line=\"207\">采用 \u003Cstrong>库存预占 + 定时释放\u003C\u002Fstrong> 机制，避免库存长期被占用；\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli data-line=\"208\">\u003Cstrong>兜底\u003C\u002Fstrong>：数据库加唯一索引或应用层校验，确保数据一致性。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"210\">此外，配合 \u003Cstrong>延迟队列\u003C\u002Fstrong> 或 \u003Cstrong>定时任务\u003C\u002Fstrong> 自动清理超时未支付订单，释放库存。”\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr data-line=\"212\">\n\u003Ch2 data-line=\"214\" id=\"六、扩展思考\">六、扩展思考\u003C\u002Fh2>\n\u003Cul data-line=\"216\">\n\u003Cli data-line=\"216\">如何处理 Token 被恶意刷取？\u003Cbr>\n→ 限流（如 Guava RateLimiter 或 Sentinel）\u003C\u002Fli>\n\u003Cli data-line=\"218\">如何保证库存预占的原子性？\u003Cbr>\n→ Redis Lua 脚本 or 分布式事务（Seata，但成本高）\u003C\u002Fli>\n\u003Cli data-line=\"220\">高并发下 Redis 成为瓶颈？\u003Cbr>\n→ 分片、本地缓存 + 二级缓存策略\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr data-line=\"223\">\n\u003Ch2 data-line=\"225\" id=\"总结\">总结\u003C\u002Fh2>\n\u003Cp data-line=\"227\">防止重复下单不是单一技术点，而是 \u003Cstrong>系统性工程\u003C\u002Fstrong>。在 Spring Boot 项目中，应结合 \u003Cstrong>Redis 幂等令牌、分布式锁、库存预占、定时清理\u003C\u002Fstrong> 等手段，构建多层次防御体系。既保障用户体验，又确保系统稳定性和数据一致性。\u003C\u002Fp>\n\u003Cblockquote data-line=\"229\">\n\u003Cp data-line=\"229\">💡 \u003Cstrong>关键点\u003C\u002Fstrong>：\u003Cstrong>幂等性是核心，分布式锁是保障，库存释放是闭环。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr data-line=\"231\">\n\u003Cp data-line=\"233\">希望这篇文章能帮助你在面试中从容应对“重复下单”类问题！\u003C\u002Fp>\n",4894,16,"0","",2,0,70,"订单,用户,库存,&quot,单","Spring Boot 如何避免用户重复下单（多次下单未支付，占用库存）？—— 面试题详解\n在高并发电商系统中，一个常见的问题是：用户短时间内多次点击“下单”按钮，生成多个未支付订单，导致库存被无效占用，影响其他用户购买。这不仅浪费系统资源，还可能引发超卖或库存不足的问题。","2025-12-29 21:20:52"]