LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

高并发下如何防止重复提交订单?

zhenglin
2026年6月4日 8:52 本文热度 53

前言

当你的用户疯狂点击提交按钮时,你的系统准备好迎接这场“连击风暴”了吗?

在电商系统的实战中,我见过太多因重复提交导致的资损事故——用户一次点击,系统却创建了多个订单,导致库存错乱、用户重复支付、客服投诉爆棚。


有些小伙伴在工作中可能遇到过这样的场景:大促期间,用户反馈“明明只点了一次,为什么扣了两次款?”

开发同学查了半天日志,发现同一个用户请求在毫秒级内真的到达了服务器两次。


今天这篇文章就跟大家聊聊高并发下防止重复提交订单,希望对你会有所帮助。


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 = new Map()


// 生成请求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)

      return Promise.reject(error)

    }

    

    // 错误处理完成后,也要从pending中移除

    if (error.config) {

      const key = generateReqKey(error.config)

      pendingRequests.delete(key)

    }

    

    return Promise.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) {

                throw new 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);

    }

}

最近为了帮助大家找工作,专门建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了20多家大厂的工作内推岗位。加苏三的微信:li_su223,备注:掘金+所在城市,即可进群。

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) NOT NULL AUTO_INCREMENT COMMENT '主键',

  `order_no` varchar(32) NOT NULL COMMENT '订单号,业务唯一',

  `user_id` bigint(20) NOT NULL COMMENT '用户ID',

  `product_id` bigint(20) NOT NULL COMMENT '商品ID',

  `quantity` int(11) NOT NULL COMMENT '购买数量',

  `amount` decimal(10,2) NOT NULL COMMENT '订单金额',

  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '订单状态:1-待支付,2-已支付',

  `submit_token` varchar(64) DEFAULT NULL COMMENT '提交Token,用于幂等',

  `version` int(11) NOT NULL DEFAULT '1' COMMENT '版本号,用于乐观锁',

  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,

  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_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());

            

            return new 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();

            return null;

        }

    }

    

    /**

     * 订单提交分布式锁

     */

    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 第五道防线:异步处理与消息队列

对于高并发场景,可以采用异步处理模式,将同步请求转为异步任务。

实现代码示例:

// 异步订单处理实现

@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 综合方案:多层次联合防护

在实际生产环境中,我们通常会采用多层次、立体化的防护策略。

以下是一个完整的综合方案流程图:

这个多层次方案中,每一层都有其特定作用:

  • 前端层:用户体验优化,拦截大部分无意识重复

  • 网关层:安全防护,防刷、限流

  • 业务层:核心幂等逻辑,分布式锁保证并发安全

  • 数据层:最终保障,唯一约束防止数据不一致

  • 异步层:削峰填谷,提升系统吞吐量

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. 监控告警必不可少:没有监控的系统就像没有仪表的飞机,无法发现问题和优化性能。

在实际架构设计中,我通常建议采用 "前端防抖 + 网关限流 + Token幂等 + 分布式锁 + 数据库唯一约束" 的综合方案,对于秒杀等极致场景再加入异步处理。

有些小伙伴可能会觉得这些防护措施太复杂,影响开发效率。

但请记住:预防的成本远低于修复的成本

一次重复提交导致的资损事故,可能就需要整个团队加班数周来修复数据和安抚用户。

技术方案没有银弹,只有最适合业务场景的平衡。



该文章在 2026/6/4 8:52:24 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号