前言

當你的用户瘋狂點擊提交按鈕時,你的系統準備好迎接這場“連擊風暴”了嗎?

在電商系統的實戰中,我見過太多因重複提交導致的資損事故——用户一次點擊,系統卻創建了多個訂單,導致庫存錯亂、用户重複支付、客服投訴爆棚。

有些小夥伴在工作中可能遇到過這樣的場景:大促期間,用户反饋“明明只點了一次,為什麼扣了兩次款?”

開發同學查了半天日誌,發現同一個用户請求在毫秒級內真的到達了服務器兩次。

今天這篇文章就跟大家聊聊高併發下防止重複提交訂單,希望對你會有所幫助。

01 為什麼會重複提交?

在深入解決方案前,我們必須搞清楚重複提交是如何發生的。

常見的場景有:

  1. 用户無意識重複點擊:網絡延遲時,用户心急多次點擊提交按鈕
  2. 前端防抖失效:前端做了防抖處理,但被繞過或配置不當
  3. 網絡超時重試:請求超時後,客户端或網關自動重試
  4. 惡意攻擊:競爭對手或黑客故意重複提交
  5. 後端處理超時:第一個請求處理慢,客户端以為失敗又發一次

來看一個典型的用户操作流程,以及其中可能發生重複的各個環節:

高併發下如何防止重複下單?_冪等

從圖中可以看到,從用户點擊到訂單落庫,幾乎每個環節都可能成為重複提交的“案發現場”。

下面,我們就針對這些環節,層層佈防。

02 第一道防線:前端防抖與按鈕控制

這是最直觀、成本最低的防護措施。

原則是:在用户交互層面儘量減少無效請求

2.1 按鈕狀態控制

// 前端防抖實現示例(Vue + Element UI)
<template>
  <el-button 
    :loading="submitting" 
    :disabled="submitting"
    @click="handleSubmitOrder"
  >
    {{ submitting ? '提交中...' : '提交訂單' }}
  </el-button>
</template>

<script>
export default {
  data() {
    return {
      submitting: false,
      submitToken: null // 用於標識當前提交的token
    }
  },
  methods: {
    async handleSubmitOrder() {
      if (this.submitting) {
        this.$message.warning('正在提交,請勿重複點擊')
        return
      }
      
      this.submitting = true
      
      try {
        // 生成唯一token,用於後端冪等性校驗
        this.submitToken = this.generateSubmitToken()
        
        const result = await this.$api.order.submit({
          orderData: this.orderData,
          submitToken: this.submitToken
        })
        
        this.$message.success('訂單提交成功')
        this.$router.push(`/order/detail/${result.orderId}`)
      } catch (error) {
        this.$message.error(`提交失敗: ${error.message}`)
        this.submitting = false // 失敗後重置狀態
      }
    },
    
    generateSubmitToken() {
      // 生成唯一標識,可以用UUID或時間戳+隨機數
      return `order_submit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    }
  }
}
</script>

2.2 請求防抖與攔截

// 使用axios攔截器實現請求防抖
import axios from'axios'

// 存儲正在進行的請求
const pendingRequests = newMap()

// 生成請求key
const generateReqKey = (config) => {
const { method, url, params, data } = config
return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}

// 請求攔截器
axios.interceptors.request.use(config => {
const key = generateReqKey(config)

if (pendingRequests.has(key)) {
    // 請求已存在,取消當前請求
    config.cancelToken = new axios.CancelToken(cancel => {
      cancel(`重複請求已被攔截: ${key}`)
    })
  } else {
    // 新請求,添加到pending中
    pendingRequests.set(key, config)
  }

return config
})

// 響應攔截器
axios.interceptors.response.use(
response => {
    const key = generateReqKey(response.config)
    pendingRequests.delete(key)
    return response
  },
  error => {
    if (axios.isCancel(error)) {
      console.log('請求被取消:', error.message)
      returnPromise.reject(error)
    }
    
    // 錯誤處理完成後,也要從pending中移除
    if (error.config) {
      const key = generateReqKey(error.config)
      pendingRequests.delete(key)
    }
    
    returnPromise.reject(error)
  }
)

前端防護小結

  • 優點:實現簡單,能攔截大部分用户無意識的重複點擊
  • 缺點:可被繞過(如直接調用API、禁用JS、使用Postman等工具)
  • 結論:前端防護是必要但不充分的措施,絕不能作為唯一防線

03 第二道防線:後端接口冪等性設計

冪等性是解決重複提交的核心理念

所謂冪等,就是同一個操作執行多次的結果與執行一次的結果相同

3.1 什麼是冪等性?

對於訂單提交接口:

  • 冪等:無論調用1次還是N次,都只創建一個訂單
  • 非冪等:調用N次可能創建N個訂單

3.2 基於Token的冪等實現

這是最常用的冪等實現方案,流程如下:

  1. 客户端在提交前,先向後端申請一個唯一Token
  2. 提交訂單時攜帶此Token
  3. 服務端檢查Token是否已使用過
// 冪等性Token服務
@Service
public class IdempotentTokenService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String IDEMPOTENT_PREFIX = "idempotent:token:";
    private static final long TOKEN_EXPIRE_SECONDS = 300; // Token有效期5分鐘
    
    /**
     * 生成冪等性Token
     * /
    public String generateToken(String userId) {
        String token = UUID.randomUUID().toString();
        String redisKey = IDEMPOTENT_PREFIX + userId + ":" + token;
        
        // 存儲Token,設置過期時間
        redisTemplate.opsForValue().set(
            redisKey, 
            "1", 
            TOKEN_EXPIRE_SECONDS, 
            TimeUnit.SECONDS
        );
        
        return token;
    }
    
    /**
     * 檢查並消費Token
     * @return true: Token有效且消費成功; false: Token無效或已消費
     */
    public boolean checkAndConsumeToken(String userId, String token) {
        String redisKey = IDEMPOTENT_PREFIX + userId + ":" + token;
        
        // 使用Lua腳本保證原子性
        String luaScript = """
            if redis.call('get', KEYS[1]) == '1' then
                redis.call('del', KEYS[1])
                return 1
            else
                return 0
            end
            """;
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        
        Long result = redisTemplate.execute(
            redisScript, 
            Collections.singletonList(redisKey)
        );
        
        return result != null && result == 1L;
    }
}

// 使用AOP實現冪等性校驗
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    String key() default ""; // 冪等鍵,支持SpEL表達式
    long expireTime() default 300; // 過期時間,秒
}

@Aspect
@Component
public class IdempotentAspect {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 1. 獲取方法參數
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 2. 解析冪等鍵(支持SpEL)
        String keyExpression = idempotent.key();
        String redisKey = parseKey(keyExpression, method, args);
        
        // 3. 嘗試獲取分佈式鎖(防止併發請求同時通過檢查)
        String lockKey = redisKey + ":lock";
        boolean lockAcquired = false;
        try {
            // 嘗試加鎖
            lockAcquired = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
            
            if (!lockAcquired) {
                thrownew BusinessException("系統繁忙,請稍後重試");
            }
            
            // 4. 檢查Token是否已使用
            Boolean exists = redisTemplate.hasKey(redisKey);
            if (Boolean.TRUE.equals(exists)) {
                // Token已使用,直接返回之前的處理結果(這裏需要根據實際業務調整)
                throw new BusinessException("請勿重複提交訂單");
            }
            
            // 5. 執行業務邏輯
            Object result = joinPoint.proceed();
            
            // 6. 標記Token已使用
            redisTemplate.opsForValue().set(
                redisKey, 
                "processed", 
                idempotent.expireTime(), 
                TimeUnit.SECONDS
            );
            
            return result;
            
        } finally {
            // 釋放鎖
            if (lockAcquired) {
                redisTemplate.delete(lockKey);
            }
        }
    }
    
    private String parseKey(String expression, Method method, Object[] args) {
        // 這裏實現SpEL表達式解析,獲取實際的冪等鍵
        // 例如可以從參數中提取userId+orderToken
        return "parsed:key:from:expression";
    }
}

// 在訂單提交接口上使用
@RestController
@RequestMapping("/order")
public class OrderController {
    
    @PostMapping("/submit")
    @Idempotent(key = "#request.userId + ':' + #request.submitToken", expireTime = 300)
    public ApiResponse<OrderSubmitResult> submitOrder(@RequestBody OrderSubmitRequest request) {
        // 這裏是真正的訂單創建邏輯
        OrderSubmitResult result = orderService.createOrder(request);
        return ApiResponse.success(result);
    }
}

3.3 基於唯一業務標識的冪等

除了Token方案,還可以利用業務的自然唯一性實現冪等:

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Transactional
    public OrderSubmitResult createOrder(OrderSubmitRequest request) {
        // 方法1:先查詢是否存在
        Order existingOrder = orderMapper.selectByUniqueKey(
            request.getUserId(), 
            request.getProductId(), 
            request.getSubmitTime()
        );
        
        if (existingOrder != null) {
            // 訂單已存在,直接返回
            return convertToResult(existingOrder);
        }
        
        // 方法2:利用數據庫唯一約束
        try {
            Order newOrder = buildOrder(request);
            orderMapper.insert(newOrder);
            return convertToResult(newOrder);
        } catch (DuplicateKeyException e) {
            // 捕獲唯一鍵衝突異常
            log.warn("訂單重複提交,uniqueKey={}", request.getUniqueKey());
            
            // 查詢已創建的訂單並返回
            Order createdOrder = orderMapper.selectByUniqueKey(
                request.getUserId(), 
                request.getProductId(), 
                request.getSubmitTime()
            );
            
            if (createdOrder == null) {
                throw new BusinessException("訂單處理異常,請稍後重試");
            }
            
            return convertToResult(createdOrder);
        }
    }
    
    // 訂單表可添加唯一索引
    // ALTER TABLE t_order ADD UNIQUE KEY uk_user_product_time (user_id, product_id, submit_time);
}

冪等性設計小結

  • Token方案:通用性強,適合大多數場景
  • 業務標識方案:更自然,但依賴業務的天然唯一性
  • 關鍵點:所有冪等性檢查必須在事務開始前完成,否則可能失效

04 第三道防線:數據庫層防護

數據庫是數據持久化的最後一道關卡,在這裏設置防護至關重要。

4.1 唯一約束與樂觀鎖

-- 訂單表設計示例
CREATE TABLE`t_order` (
`id`bigint(20) NOTNULL AUTO_INCREMENT COMMENT'主鍵',
`order_no`varchar(32) NOTNULLCOMMENT'訂單號,業務唯一',
`user_id`bigint(20) NOTNULLCOMMENT'用户ID',
`product_id`bigint(20) NOTNULLCOMMENT'商品ID',
`quantity`int(11) NOTNULLCOMMENT'購買數量',
`amount`decimal(10,2) NOTNULLCOMMENT'訂單金額',
`status`tinyint(4) NOTNULLDEFAULT'1'COMMENT'訂單狀態:1-待支付,2-已支付',
`submit_token`varchar(64) DEFAULTNULLCOMMENT'提交Token,用於冪等',
`version`int(11) NOTNULLDEFAULT'1'COMMENT'版本號,用於樂觀鎖',
`create_time` datetime NOTNULLDEFAULTCURRENT_TIMESTAMP,
`update_time` datetime NOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
UNIQUE KEY`uk_order_no` (`order_no`), -- 訂單號唯一
UNIQUE KEY`uk_user_submit_token` (`user_id`, `submit_token`), -- 提交Token唯一
UNIQUE KEY`uk_user_product_time` (`user_id`, `product_id`, `create_time`), -- 業務維度唯一
KEY`idx_user_id` (`user_id`),
KEY`idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='訂單表';

4.2 數據庫層面的冪等實現

// 使用數據庫事務+唯一約束保證最終一致性
@Service
public class OrderServiceV2 {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private IdempotentTokenService tokenService;
    
    @Transactional(rollbackFor = Exception.class)
    public OrderSubmitResult submitOrderWithDBProtection(OrderSubmitRequest request) {
        String userId = request.getUserId();
        String submitToken = request.getSubmitToken();
        
        // 1. 檢查冪等Token(在事務外先檢查一次)
        if (!tokenService.checkAndConsumeToken(userId, submitToken)) {
            throw new BusinessException("請勿重複提交訂單");
        }
        
        try {
            // 2. 生成訂單號(雪花算法等分佈式ID生成器)
            String orderNo = generateOrderNo();
            
            // 3. 創建訂單對象
            Order order = new Order();
            order.setOrderNo(orderNo);
            order.setUserId(userId);
            order.setProductId(request.getProductId());
            order.setQuantity(request.getQuantity());
            order.setAmount(calculateAmount(request));
            order.setSubmitToken(submitToken);
            
            // 4. 插入訂單(這裏依賴數據庫唯一約束)
            orderMapper.insert(order);
            
            // 5. 更新庫存等後續操作...
            updateProductStock(request.getProductId(), request.getQuantity());
            
            returnnew OrderSubmitResult(orderNo, "訂單創建成功");
            
        } catch (DuplicateKeyException e) {
            // 6. 處理唯一約束衝突
            log.warn("訂單重複提交,userId={}, token={}", userId, submitToken);
            
            // 查詢已創建的訂單
            Order existingOrder = orderMapper.selectBySubmitToken(userId, submitToken);
            if (existingOrder != null) {
                return new OrderSubmitResult(
                    existingOrder.getOrderNo(), 
                    "訂單已創建成功,請勿重複提交"
                );
            }
            
            // 理論上不會走到這裏,除非有極端情況
            throw new BusinessException("訂單處理異常,請稍後重試");
        }
    }
}

05 第四道防線:分佈式鎖

在分佈式環境下,多個實例可能同時處理同一個請求,需要分佈式鎖來保證只有一個實例執行核心邏輯。

5.1 基於Redis的分佈式鎖

@Component
public class DistributedLockService {
    
    @Autowired
    private RedissonClient redissonClient;
    
    /**
     * 嘗試獲取分佈式鎖
     * @param lockKey 鎖的key
     * @param waitTime 等待時間(毫秒)
     * @param leaseTime 持有時間(毫秒)
     * @return 鎖對象,獲取失敗返回null
     */
    public RLock tryLock(String lockKey, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
            return acquired ? lock : null;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            returnnull;
        }
    }
    
    /**
     * 訂單提交分佈式鎖
     */
    public RLock lockForOrderSubmit(String userId, String submitToken) {
        String lockKey = String.format("order:submit:lock:%s:%s", userId, submitToken);
        return tryLock(lockKey, 100, 5000); // 等待100ms,鎖持有5秒
    }
}

// 在訂單服務中使用分佈式鎖
@Service
public class OrderServiceV3 {
    
    @Autowired
    private DistributedLockService lockService;
    
    @Autowired
    private OrderMapper orderMapper;
    
    public OrderSubmitResult submitOrderWithDistributedLock(OrderSubmitRequest request) {
        String userId = request.getUserId();
        String submitToken = request.getSubmitToken();
        
        // 1. 獲取分佈式鎖
        RLock lock = lockService.lockForOrderSubmit(userId, submitToken);
        if (lock == null) {
            throw new BusinessException("系統繁忙,請稍後重試");
        }
        
        try {
            // 2. 檢查是否已處理
            Order existingOrder = orderMapper.selectBySubmitToken(userId, submitToken);
            if (existingOrder != null) {
                return new OrderSubmitResult(
                    existingOrder.getOrderNo(), 
                    "訂單已創建成功,請勿重複提交"
                );
            }
            
            // 3. 執行業務邏輯
            return doCreateOrder(request);
            
        } finally {
            // 4. 釋放鎖
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    
    private OrderSubmitResult doCreateOrder(OrderSubmitRequest request) {
        // 實際的訂單創建邏輯
        // 這裏已經保證了同一時刻只有一個線程在處理同一個提交請求
        // ...
    }
}

5.2 分佈式鎖的注意事項

使用分佈式鎖時要注意:

  1. 鎖粒度:不要太粗(影響性能)也不要太細(增加複雜度)
  2. 鎖超時:必須設置合理的超時時間,防止死鎖
  3. 鎖續期:對於長時間操作,需要實現鎖續期機制
  4. 可重入性:同一個線程可以重複獲取鎖
  5. 容錯性:Redis集羣故障時要有降級方案

06 第五道防線:異步處理與消息隊列

對於高併發場景,可以採用異步處理模式,將同步請求轉為異步任務。

高併發下如何防止重複下單?_冪等_02

實現代碼示例:

// 異步訂單處理實現
@Component
public class AsyncOrderService {
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    /**
     * 異步提交訂單
     */
    public AsyncSubmitResult asyncSubmitOrder(OrderSubmitRequest request) {
        // 1. 生成唯一請求ID
        String requestId = generateRequestId(request.getUserId());
        
        // 2. 快速驗證(庫存、用户狀態等)
        quickValidate(request);
        
        // 3. 將請求ID與用户關聯(用於查詢結果)
        String pendingKey = "order:pending:" + request.getUserId() + ":" + requestId;
        redisTemplate.opsForValue().set(pendingKey, "processing", 10, TimeUnit.MINUTES);
        
        // 4. 發送到消息隊列
        OrderMessage message = new OrderMessage();
        message.setRequestId(requestId);
        message.setRequest(request);
        message.setTimestamp(System.currentTimeMillis());
        
        rocketMQTemplate.asyncSend(
            "ORDER_SUBMIT_TOPIC", 
            message, 
            new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    log.info("訂單消息發送成功: {}", requestId);
                }
                
                @Override
                public void onException(Throwable throwable) {
                    log.error("訂單消息發送失敗: {}", requestId, throwable);
                    // 發送失敗,更新狀態
                    redisTemplate.opsForValue().set(
                        pendingKey, 
                        "failed", 
                        5, 
                        TimeUnit.MINUTES
                    );
                }
            }
        );
        
        // 5. 立即返回,告知用户處理中
        return new AsyncSubmitResult(requestId, "訂單提交成功,正在處理中");
    }
}

// 消息消費者
@Component
@RocketMQMessageListener(
    topic = "ORDER_SUBMIT_TOPIC",
    consumerGroup = "order-submit-consumer-group"
)
public class OrderSubmitConsumer implements RocketMQListener<OrderMessage> {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Override
    public void onMessage(OrderMessage message) {
        String requestId = message.getRequestId();
        OrderSubmitRequest request = message.getRequest();
        
        // 1. 冪等檢查(基於requestId)
        Order existing = orderMapper.selectByRequestId(requestId);
        if (existing != null) {
            log.info("訂單已處理,跳過: {}", requestId);
            return;
        }
        
        // 2. 創建訂單
        Order order = createOrder(request, requestId);
        
        try {
            orderMapper.insert(order);
            log.info("訂單創建成功: {}", order.getOrderNo());
            
            // 3. 更新處理狀態
            updateProcessingStatus(request.getUserId(), requestId, "success", order.getOrderNo());
            
        } catch (DuplicateKeyException e) {
            log.warn("訂單重複,requestId={}", requestId);
            // 查詢已創建的訂單
            Order created = orderMapper.selectByRequestId(requestId);
            if (created != null) {
                updateProcessingStatus(request.getUserId(), requestId, "success", created.getOrderNo());
            }
        }
    }
}

07 綜合方案:多層次聯合防護

在實際生產環境中,我們通常會採用多層次、立體化的防護策略。

以下是一個完整的綜合方案流程圖:

高併發下如何防止重複下單?_重複提交_03

這個多層次方案中,每一層都有其特定作用:

  • 前端層:用户體驗優化,攔截大部分無意識重複
  • 網關層:安全防護,防刷、限流
  • 業務層:核心冪等邏輯,分佈式鎖保證併發安全
  • 數據層:最終保障,唯一約束防止數據不一致
  • 異步層:削峯填谷,提升系統吞吐量

08 實戰:不同場景下的方案選擇

不同的業務場景需要不同的防護策略,這裏給出一些實踐建議:

8.1 普通電商訂單

// 普通電商訂單推薦方案
@Service
public class StandardOrderService {
    
    // 綜合使用:前端防抖 + Token冪等 + 數據庫唯一約束
    public OrderSubmitResult submitStandardOrder(OrderSubmitRequest request) {
        // 1. 參數校驗
        validateRequest(request);
        
        // 2. 冪等Token檢查(Redis)
        if (!idempotentCheck(request.getUserId(), request.getSubmitToken())) {
            return getExistingOrderResult(request.getUserId(), request.getSubmitToken());
        }
        
        // 3. 分佈式鎖(防併發)
        RLock lock = acquireOrderLock(request.getUserId(), request.getProductId());
        try {
            // 4. 庫存檢查等業務校驗
            checkInventory(request.getProductId(), request.getQuantity());
            
            // 5. 創建訂單(依賴數據庫唯一約束)
            return createOrderInTransaction(request);
        } finally {
            lock.unlock();
        }
    }
}

8.2 秒殺訂單

// 秒殺訂單需要更極致的優化
@Service
public class FlashSaleOrderService {
    
    // 秒殺方案:異步處理 + 庫存預扣 + 最終一致性
    public FlashSaleSubmitResult submitFlashSaleOrder(FlashSaleRequest request) {
        // 1. 驗證用户資格和活動狀態(緩存中檢查)
        if (!checkUserQualification(request.getUserId(), request.getActivityId())) {
            throw new BusinessException("您不具備參與資格");
        }
        
        // 2. 預扣庫存(Redis原子操作)
        boolean stockDeducted = preDeductStock(
            request.getActivityId(), 
            request.getProductId(), 
            request.getUserId()
        );
        
        if (!stockDeducted) {
            throw new BusinessException("庫存不足");
        }
        
        // 3. 生成唯一請求ID
        String requestId = generateRequestId(request.getUserId(), request.getActivityId());
        
        // 4. 發送到消息隊列(快速返回)
        sendToMQ(request, requestId);
        
        // 5. 立即返回
        return new FlashSaleSubmitResult(requestId, "秒殺請求已接受,處理中");
    }
    
    // 消費者異步創建訂單
    @Transactional
    public void processFlashSaleOrder(FlashSaleRequest request, String requestId) {
        // 這裏只需要處理真正的訂單創建
        // 因為庫存已在Redis中預扣,只需保證最終一致性
        try {
            createOrder(request, requestId);
            // 同步庫存到數據庫
            syncStockToDB(request.getProductId(), request.getActivityId());
        } catch (Exception e) {
            // 失敗時回滾Redis庫存
            rollbackStockInRedis(request.getActivityId(), request.getProductId(), request.getUserId());
            throw e;
        }
    }
}

10 總結

防止重複提交訂單是一個系統工程,需要從前到後、多層次的防護。

讓我們回顧一下關鍵點:

  1. 前端防護是體驗,不是保障:按鈕防抖、請求攔截能改善用户體驗,但不能作為唯一防線。
  2. 冪等性是核心理念:無論是Token方案還是業務唯一標識,都要保證同一操作執行多次的結果一致。
  3. 分佈式鎖解決併發問題:在分佈式環境下,防止多個實例同時處理同一請求。
  4. 數據庫是最後防線:唯一約束、樂觀鎖等機制能在應用層防護失效時保證數據一致性。
  5. 異步處理提升吞吐:對於高併發場景,將同步請求轉為異步處理,提高系統整體吞吐量。
  6. 監控告警必不可少:沒有監控的系統就像沒有儀表的飛機,無法發現問題和優化性能。