订单超时如何自动取消

订单超时如何自动取消

核心本质 ✨

订单超时自动处理的本质是延迟触发状态变更 + 业务兜底,必须解决三个核心问题:

  1. 什么时候触发超时逻辑?(时间精度)
  2. 怎么保证触发不丢?(可靠性)
  3. 触发失败了怎么办?(容错性)

主流方案对比 📊

我整理了四种工业界常用的方案,各有优劣:

方案 实现难度 时间精度 单机性能 可靠性 适用场景
数据库轮询(定时任务扫表) 低 ✅ 差 ❌(分钟级) 低 ❌ 中 ⚠️ 小流量、非核心业务
JDK DelayQueue 高 ✅ 低 ❌(进程重启丢失) 单体应用、临时场景
Redis ZSet(score = 超时时间) 较高 ✅ 高 ✅ 中 ⚠️ 中小流量、分布式场景
MQ 延迟消息(RocketMQ/RabbitMQ) 高 ✅ 高 ✅ 高 ✅ 大厂核心业务、高并发场景

大厂最优落地实现 🚀

互联网大厂主流采用 RocketMQ 定时消息 + 本地事务表 + 定时任务兜底 的组合方案,兼顾性能、可靠性和可维护性。

核心流程图

graph LR A用户下单 --> B创建订单(状态:待支付) B --> C写入本地事务表(订单号+超时时间) C --> D发送RocketMQ 15分钟延迟消息 D --> EMQ Broker存储延迟消息 E --> F15分钟后投递到消费者 F --> G查询订单状态 G --> H{是否已支付?} H -->|是| I标记事务表为已完成,结束流程 H -->|否| J获取Redis分布式锁 J --> K取消订单+恢复库存+释放优惠券 K --> L标记事务表为已完成 L --> M发送订单取消通知(短信/推送)

关键实现细节

  1. 事务消息保证原子性:使用 RocketMQ 事务消息,确保 "创建订单" 和 "发送延迟消息" 要么同时成功,要么同时失败,避免消息丢失。
  2. 延迟消息精度:RocketMQ 支持 18 个固定延迟级别(1s-2h),4.x + 版本支持自定义任意时间延迟,完全满足订单超时需求。
  3. 本地事务表:记录每个订单的超时处理状态,用于幂等校验和兜底扫描。

高可用保障机制 🛡️

这部分是区分初级和高级开发的关键,必须覆盖:

  1. 幂等性设计:以订单号作为唯一标识,处理前先检查事务表状态,避免重复取消订单。
  2. 消息防丢失:开启 MQ 持久化 + 生产者 ACK + 消费者手动 ACK,确保消息不丢。
  3. 分布式锁:使用 Redis 分布式锁(带过期时间 + 看门狗),防止集群环境下多个消费者同时处理同一个订单。
  4. 监控告警:监控延迟消息堆积量、订单取消成功率、异常订单数,超过阈值立即告警。

兜底方案 ⚠️

任何分布式系统都没有 100% 的可靠性,必须设计兜底机制:

  1. 定时任务兜底扫描:每天凌晨用 XXL-JOB 扫描近 3 天内 "待支付" 且超时的订单,进行补偿处理。
  2. 人工干预后台:提供订单管理后台,支持运营手动取消异常订单。

常见踩坑点 💣

  1. 服务器时间同步问题:所有服务器必须同步 NTP 时间,否则会出现超时时间计算错误。
  2. 延迟消息堆积:如果下单量突增,延迟消息可能堆积,需要提前扩容消费者集群。
  3. 库存超卖:取消订单恢复库存时,必须使用乐观锁,防止库存超卖。
  4. 状态机混乱:严格定义订单状态流转规则,禁止逆向状态变更(比如已支付订单不能被取消)。

现场模拟面试

面试官:假设我们现在要做一个电商下单功能,用户下单后30分钟内没付款,订单就要自动取消,库存也要释放。你作为一个后端开发,怎么设计这个"订单超时自动处理"的方案?不用太紧张,想到哪说到哪,我来追问。

1. 先抓本质:这是一个"延时任务"问题

本质是:一个任务需要在未来的某个指定时间点触发执行,而且只执行一次。

下单那一刻,我就知道它会在 当前时间 + 30分钟 后需要被检查。所以核心诉求是:

  • 精度:不能偏差太大,秒级比较理想。
  • 可靠性:服务重启、宕机,任务不能丢。
  • 扩展性:随着单量增长,任务不能成为瓶颈。

让我把常见的解法从上到下捋一遍,你就能看到大厂真实选型思路。

2. 初级方案(直接能想到,但都有坑)

🔸 定时任务扫表

每分钟跑一次 Job,扫描订单表 WHERE status=待支付 AND create_time < NOW() - 30分钟

优点:实现简单,无额外依赖。

缺点

  • 扫描全表,数据量大了之后像开拖拉机进高速 🚜💨
  • 时效性差,最差情况多等 59 秒
  • 锁竞争、DB 压力都是问题

结论:只适合小项目、原型阶段,面试时提一嘴就行。

3. 进阶方案(有中间件加持)

这些才是面试官想听到的精华,我画个流程图让你先有整体感觉。
flowchart LR A用户下单 --> B生成延时任务\订单30min后取消 B --> C{选择延时方案} C --> DDelayQueue / 时间轮 C --> ERabbitMQ 死信队列 C --> FRedis 过期回调 D & E & F --> G触发取消逻辑\关单/回滚库存

下面我把表格对比先甩出来,再逐一深挖 ⚡

方案 核心原理 精度 可靠性 适用量级 大厂使用度
JDK DelayQueue 内存延时队列,堆排序 毫秒级 ❌ 重启丢任务 单机万级 几乎没有
Netty 时间轮 环形数组 + 刻度轮转 毫秒级 ❌ 内存态 单机十万级 自研中间件内核
RabbitMQ 死信队列 TTL 到期转入死信 秒级 ✅ 消息持久化 集群支撑百万 ⭐⭐⭐⭐⭐
Redis 过期回调 键过期,订阅 __keyevent@*__:expired 秒级 ⚠️ 不保证必达 中等 ⭐⭐⭐
RocketMQ 延迟消息 预定义延迟级别 秒级 ✅ 高可靠 超大 ⭐⭐⭐⭐⭐

4. 重点方案深挖

🔥 4.1 RabbitMQ 死信队列(最经典)

我遇到很多中大型项目就用这个,稳定、易懂。

原理流程:

flowchart TD A下单成功 --> B发送消息到 延时队列\设置 TTL=30min B --> C{消息在延时队列<br>存活 30min} C -->|TTL到期| D自动转入 死信交换机 D --> E路由到 死信队列 E --> F消费者监听\执行:检查订单状态 F --> G{已支付?} G -->|是| H忽略/确认消息ACK G -->|否| I关闭订单\释放库存\ACK消息

代码小样(思路版):

java 复制代码
// 声明延时队列(设置消息TTL和死信交换机)
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "order.dlx.exchange");
args.put("x-message-ttl", 30 * 60 * 1000); // 30分钟
channel.queueDeclare("order.delay.queue", true, false, false, args);

// 消费者监听死信队列
@RabbitListener(queues = "order.dlx.queue")
public void handleTimeout(OrderMessage msg) {
    Order order = orderService.getById(msg.getOrderId());
    if (order.getStatus() == UNPAID) {
        orderService.cancelOrder(order);
        inventoryService.release(order);
    }
}

优点:消息持久化到磁盘,服务重启不丢;集群天然高可用。

注意点 :如果不同订单需要不同延迟时间(比如有的15分钟,有的30分钟),要么不同队列,要么用插件 rabbitmq_delayed_message_exchange 实现更灵活的延迟。

大厂很爱用。

🚀 4.2 RocketMQ 延迟消息(阿里系常用)

RocketMQ 自带延迟级别:1s 5s 10s 30s 1m 2m ... 1h,虽然不能精确指定任意秒,但30分钟刚好落在级别 18 (30m)。

用法极简:

java 复制代码
Message msg = new Message("ORDER_TOPIC", orderJson.getBytes());
msg.setDelayTimeLevel(18); // 30分钟
producer.send(msg);

消费者收到消息时,检查订单状态,该关就关。

好处:代码入侵极小,吞吐高。

限制:延迟级别是预定义的,定制需修改 Broker 配置。

⚡ 4.3 Redis 过期回调(巧劲大,但不硬)

利用 Redis 的键空间通知:开启 notify-keyspace-events Ex 后,键过期会发布消息。

java 复制代码
// 下单时
redisTemplate.opsForValue().set("order:timeout:" + orderId, "", 30, TimeUnit.MINUTES);

// 监听
@EventListener
public void handleExpired(RedisKeyExpiredEvent event) {
    String key = new String(event.getSource());
    if (key.startsWith("order:timeout:")) {
        // 关单逻辑...
    }
}

⚠️ 致命伤 :Redis 的过期键清理策略是惰性+定期,通知不保证准时、不保证必达。一旦漏了,订单就成僵尸了。

实际用法:作为辅助通道,配合定时任务兜底扫描(所谓"扫把方案" 🧹)。

🧠 4.4 时间轮(Netty HashedWheelTimer)

自研中间件时常见,比如某个大厂自己写了个延时消息服务,底层就是时间轮。

核心思想:一个环形数组,每个槽位是某个时间刻度上的任务链表,指针每隔一个 tick 走一格,走到哪个槽就执行里面的任务。
flowchart LR subgraph 时间轮 direction LR slot1"槽0" --> slot2"槽1" --> slot3"槽2" --> slot4"..." --> slot5"槽n-1" end pointer当前指针 -.-> slot2 slot2 -->|挂着| task1订单1 取消任务 slot2 -->|挂着| task2订单2 取消任务

优点 :插入/移除 O(1),性能极高,非常适合海量定时任务。

缺点:纯内存结构,需要自己实现持久化,否则重启丢任务。

👉 面试时如果能聊"时间轮 + 磁盘日志/Redis 备份"这种组合方案,就是加分项。

5. 那到底怎么选?给你一个接地气的标准

📌 真实项目我这么推荐

  • 中小项目快速落地:RabbitMQ 死信队列 + 一个兜底扫表 Job(防丢消息)
  • 阿里技术栈/高并发:RocketMQ 延迟消息 + 扫表补偿
  • 自研中间件的团队:时间轮 + RocksDB 做持久化,打造通用的延时任务平台
  • 想要极简:Redis 过期通知 + 定时任务兜底(别光靠 Redis 通知)

兜底思想永远不能丢:任何消息中间件都不能保证 100% 不丢消息,全链路里一定要有个"终极扫帚" 🧹 定时扫描超时订单,保证最终一致性。

6. 面试收尾金句

如果我在面试时被问到,最后会这样总结:

"这个问题的本质是个延时任务 ,上面几个方案的差别在于精度、可靠性和成本 。实际选型要结合业务容忍度、现有基础设施来定,但无论如何,消息队列+定时补偿的双保险机制,是工业界验证过的稳妥打法。"

这样说既体现了深度,又展示了经验,还暴露了你考虑过坑和边界 ------ 面试官很难不点头 👍。

核心实现代码(技术亮点标注)💻

1. 订单创建 + RocketMQ 事务消息发送(核心亮点:原子性保证)

java 复制代码
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderTransactionMapper transactionMapper;

    @Override
    public String createOrder(OrderCreateDTO dto) {
        // 1. 生成唯一订单号(雪花算法)
        String orderNo= IdGenerator.nextIdStr();
        
        // 2. 构建订单对象(状态:待支付)
        Order order= Order.builder()
                .orderNo(orderNo)
                .userId(dto.getUserId())
                .amount(dto.getAmount())
                .status(OrderStatusEnum.PENDING_PAYMENT.getCode())
                .expireTime(LocalDateTime.now().plusMinutes(15)) // 动态超时时间
                .build();
        
        // 3. 构建本地事务记录(核心:幂等+兜底)
        OrderTransaction transaction= OrderTransaction.builder()
                .orderNo(orderNo)
                .transactionType(TransactionTypeEnum.ORDER_TIMEOUT_CANCEL.getCode())
                .status(TransactionStatusEnum.PENDING.getCode())
                .expireTime(order.getExpireTime())
                .build();
        
        // 4. 发送RocketMQ事务消息(半消息)
        // 技术亮点:保证"创建订单"和"发送延迟消息"的原子性
        rocketMQTemplate.sendMessageInTransaction(
                "order-timeout-topic:cancel",
                MessageBuilder.withPayload(orderNo)
                        .setDelayTimeLevel(16) // 15分钟延迟(RocketMQ默认级别)
                        .build(),
                new TransactionCallback() {
                    @Override
                    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                        try {
                            // 本地事务:同时写入订单表和事务表
                            orderMapper.insert(order);
                            transactionMapper.insert(transaction);
                            return LocalTransactionState.COMMIT_MESSAGE;
                        } catch (Exception e) {
                            log.error("订单创建本地事务失败,orderNo:{}", orderNo, e);
                            return LocalTransactionState.ROLLBACK_MESSAGE;
                        }
                    }

                    @Override
                    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                        // 回查逻辑:检查本地事务是否执行成功
                        String orderNo= new String(msg.getBody());
                        OrderTransaction transaction= transactionMapper.selectByOrderNo(orderNo);
                        return transaction!= null? 
                                LocalTransactionState.COMMIT_MESSAGE : 
                                LocalTransactionState.ROLLBACK_MESSAGE;
                    }
                }
        );

        return orderNo;
    }
}

2. 延迟消息消费者(核心亮点:幂等性 + 分布式锁 + 状态机校验)

java 复制代码
@Component
@RocketMQMessageListener(topic= "order-timeout-topic", consumerGroup= "order-timeout-cancel-group")
public class OrderTimeoutCancelConsumer implements RocketMQListener<String> {

    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderTransactionMapper transactionMapper;
    @Autowired
    private RedisDistributedLock distributedLock;

    @Override
    public void onMessage(String orderNo) {
        // 1. 幂等性校验(第一道防线)
        OrderTransaction transaction= transactionMapper.selectByOrderNo(orderNo);
        if (transaction== null || transaction.getStatus()!= TransactionStatusEnum.PENDING.getCode()) {
            log.info("订单超时处理已完成或不存在,orderNo:{}", orderNo);
            return;
        }

        // 2. 获取分布式锁(第二道防线:防止集群重复处理)
        // 技术亮点:带看门狗的Redis分布式锁,自动续期防止业务未执行完锁过期
        String lockKey= "lock:order:cancel:"+ orderNo;
        try (RLock lock= distributedLock.getLock(lockKey)) {
            // 尝试加锁,最多等待3秒,锁默认30秒过期(看门狗自动续期)
            if (!lock.tryLock(3, TimeUnit.SECONDS)) {
                log.warn("其他节点正在处理该订单,orderNo:{}", orderNo);
                return;
            }

            // 3. 二次幂等校验(防止锁等待期间其他节点已处理)
            transaction= transactionMapper.selectByOrderNo(orderNo);
            if (transaction.getStatus()!= TransactionStatusEnum.PENDING.getCode()) {
                log.info("订单已被其他节点处理,orderNo:{}", orderNo);
                return;
            }

            // 4. 执行订单取消业务
            orderService.cancelOrderTimeout(orderNo);

            // 5. 更新事务表状态(标记为已完成)
            transactionMapper.updateStatus(orderNo, TransactionStatusEnum.COMPLETED.getCode());

            log.info("订单超时取消成功,orderNo:{}", orderNo);
        } catch (Exception e) {
            log.error("订单超时取消失败,orderNo:{}", orderNo, e);
            // 抛出异常,让MQ重新投递(最多重试16次)
            throw new RuntimeException("订单超时取消失败", e);
        }
    }
}

3. 订单取消核心业务逻辑(核心亮点:状态机严格校验 + 最终一致性)

java 复制代码
@Override
@Transactional(rollbackFor= Exception.class)
public void cancelOrderTimeout(String orderNo) {
    // 1. 查询订单
    Order order= orderMapper.selectByOrderNo(orderNo);
    if (order== null) {
        throw new BusinessException("订单不存在");
    }

    // 2. 状态机严格校验(核心:禁止逆向状态变更)
    // 技术亮点:只有"待支付"状态才能被超时取消,防止状态混乱
    if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
        log.info("订单状态不是待支付,无法取消,orderNo:{}, status:{}", orderNo, order.getStatus());
        return;
    }

    // 3. 更新订单状态为已取消
    int rows= orderMapper.updateStatus(
            orderNo,
            OrderStatusEnum.PENDING_PAYMENT.getCode(),
            OrderStatusEnum.CANCELLED_TIMEOUT.getCode()
    );

    // 乐观锁更新失败,说明其他线程已修改订单状态
    if (rows== 0) {
        log.info("订单状态已被修改,取消失败,orderNo:{}", orderNo);
        return;
    }

    // 4. 恢复库存(调用库存服务,异步+最终一致性)
    stockService.recoverStock(order.getProductId(), order.getQuantity());

    // 5. 释放优惠券(调用优惠券服务)
    couponService.releaseCoupon(order.getCouponId());

    // 6. 发送订单取消通知
    notifyService.sendOrderCancelNotify(order.getUserId(), orderNo);
}

4. RabbitMQ 延迟消息插件版(推荐 ⭐)

使用插件 rabbitmq_delayed_message_exchange,不再依赖死信队列,延迟时间可自由指定。

技术亮点

  • 利用延迟交换机直接设置 x-delay,一个交换机搞定所有延迟
  • 利用 CorrelationData 实现消息追踪
  • 消费者侧做 幂等处理 + 分布式锁,防止重复关单
java 复制代码
// ========== 配置延迟交换机 ==========
@Configuration
public class RabbitDelayConfig {
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct"); // 底层用 direct
        return new CustomExchange("order.delay.exchange",
                "x-delayed-message", true, false, args);
    }

    @Bean
    public Queue delayQueue() {
        return new Queue("order.delay.queue", true);
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(delayQueue())
                .to(delayExchange())
                .with("order.timeout")
                .noargs();
    }
}

// ========== 生产者:下单时发送延迟消息 ==========
@Service
public class OrderTimeoutProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendTimeoutMsg(String orderId, long delayMs) {
        OrderTimeoutMsg msg = new OrderTimeoutMsg(orderId, System.currentTimeMillis());
        rabbitTemplate.convertAndSend(
                "order.delay.exchange",
                "order.timeout",
                msg,
                message -> {
                    MessageProperties props = message.getMessageProperties();
                    props.setDelay((int) delayMs);          // 亮点:任意延迟时间
                    props.setMessageId(orderId);            // 用于追踪
                    props.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 持久化
                    return message;
                },
                new CorrelationData(orderId)                // 发布确认用
        );
    }
}

// ========== 消费者:幂等关单 + 分布式锁 ==========
@Component
@RabbitListener(queues = "order.delay.queue")
public class OrderTimeoutConsumer {
    @Autowired
    private OrderService orderService;
    @Autowired
    private RedissonClient redisson;

    @RabbitHandler
    public void handle(OrderTimeoutMsg msg, Channel channel, 
                       @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        String orderId = msg.getOrderId();
        RLock lock = redisson.getLock("order:lock:" + orderId);
        try {
            if (!lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                // 抢不到锁直接确认消息,避免重试堆积
                channel.basicAck(tag, false);
                return;
            }
            // 双重检查:再次从DB确认订单仍为待支付
            Order order = orderService.getById(orderId);
            if (order != null && order.getStatus() == OrderStatus.UNPAID) {
                orderService.cancelOrderTransactional(orderId); // 事务内关单+释放库存
            }
            channel.basicAck(tag, false);
        } catch (Exception e) {
            // 异常 NACK 并重试
            channel.basicNack(tag, false, true);
        } finally {
            if (lock.isHeldByCurrentThread()) lock.unlock();
        }
    }
}

亮点解读

  • x-delayed-type 让延迟交换机通用;
  • 消息ID + CorrelationData 可做全链路追踪;
  • 分布式锁 + 双重检查 = 消费端绝对幂等
  • 异常时 NACK 重回队列,保证最终处理。

5. 时间轮 + RocksDB 自研延时服务(展示硬实力)

如果面试官问"不用MQ,你们能自己写一个吗",这个代码框架能镇场。

java 复制代码
// 精简版时间轮核心,展示数据结构
public class HashedWheelTimer {
    private final WheelBucket[] wheel;          // 槽位数组
    private final long tickDurationMs;          // 每格时长
    private final int mask;
    private volatile int cursor = 0;            // 当前指针
    private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); // JDK21+

    public HashedWheelTimer(int ticksPerWheel, long tickDuration, TimeUnit unit) {
        this.tickDurationMs = unit.toMillis(tickDuration);
        this.wheel = new WheelBucket[ticksPerWheel];
        for (int i = 0; i < ticksPerWheel; i++) wheel[i] = new WheelBucket();
        this.mask = ticksPerWheel - 1;
        start();
    }

    // 添加延时任务,返回任务ID用于取消
    public String addTask(TimerTask task, long delayMs) {
        int ticks = (int) (delayMs / tickDurationMs);
        int stopIndex = (cursor + ticks) & mask;
        wheel[stopIndex].addTask(task);
        RocksDurableStore.save(task);  // 🔥 亮点:立即持久化到磁盘
        return task.getId();
    }

    private void start() {
        Thread.ofPlatform().daemon().start(() -> {
            while (true) {
                WheelBucket bucket = wheel[cursor];
                bucket.expireAll().forEach(task -> {
                    executor.submit(() -> {
                        if (task.tryLock()) {    // 🔥 分布式环境下仅单机执行
                            task.run();
                            RocksDurableStore.remove(task.getId());
                        }
                    });
                });
                cursor = (cursor + 1) & mask;
                Thread.sleep(tickDurationMs);
            }
        });
    }
}

亮点

  • 时间轮 O(1) 插入/删除,10万任务丝滑;
  • 落地 RocksDB 防重启丢失;
  • 分布式锁 task.tryLock() 保证多个实例不重复执行;
  • 虚拟线程执行,避免阻塞时间轮指针推进。

核心技术难点及解决方案 🎯

我整理了该场景下7 个最容易被面试官追问的技术难点,以及对应的工业级解决方案:

技术难点 问题描述 解决方案 技术亮点
动态超时时间配置 不同商品 / 活动 / 用户等级需要不同的超时时间(如秒杀订单 5 分钟,普通订单 15 分钟) 1. 使用 Nacos/Apollo 配置中心存储超时规则2. 订单创建时根据规则动态计算超时时间3. RocketMQ 4.9 + 支持自定义任意时间延迟 支持热更新,无需重启服务;粒度可细化到单个订单
高并发下的性能瓶颈 百万级订单量下,延迟消息堆积、消费者处理慢 1. MQ 主题分区扩容,消费者集群水平扩展2. 消费者端使用线程池批量处理3. 非核心逻辑(如通知)异步化 单机 QPS 可达 1000+,支持千万级订单量
分布式事务一致性 取消订单时,库存、优惠券、积分可能出现不一致 1. 采用 "本地事务表 + 消息重试 + 定时补偿" 的最终一致性方案2. 每个资源操作都设计幂等接口3. 提供人工补偿后台 保证数据最终一致,成功率 99.99% 以上
消息丢失与重复消费 MQ 集群故障、网络波动导致消息丢失或重复 1. 开启 MQ 持久化 + 生产者 ACK + 消费者手动 ACK2. 三重幂等防护:事务表 + 分布式锁 + 乐观锁3. 死信队列处理消费失败的消息 消息零丢失,重复消费无副作用
兜底定时任务性能优化 百万级订单量下,全表扫描会导致数据库压力过大 1. 按时间范围分片扫描(每次只扫 1 小时内的数据)2. 使用分页查询 + 游标查询避免深分页3. 多线程并行处理不同分片 扫描千万级数据仅需几分钟,数据库 CPU 使用率 < 30%
订单状态机混乱 并发操作导致订单状态逆向变更(如已支付订单被取消) 1. 严格定义订单状态流转图2. 所有状态变更都使用乐观锁3. 数据库层面添加状态约束 彻底杜绝状态机混乱问题
异常场景的容错与降级 MQ 集群整体挂掉,导致所有超时订单无法处理 1. 降级为 Redis ZSet 方案作为临时替代2. 增加兜底定时任务的执行频率(从每天 1 次改为每小时 1 次)3. 监控告警,及时通知运维人员 实现故障自动降级,保证业务连续性

追问应对示例:

面试官:如果关单服务正在执行释放库存,突然网络断开了,会发生什么?

回答:库存释放和订单取消应该放在同一个本地事务 里,使用 @Transactional。如果调用库存服务的RPC超时,我会采用重试+幂等 ,库存接口本身支持幂等(传唯一流水号)。重试多次失败后,记录异常表,定时任务扫描补偿,并告警人工介入。最终保证最终一致性

【别走,交个朋友】

我是Rain ,一个喜欢把复杂技术讲透、让代码落地的实践者。

如果你厌倦了四处收集碎片化的八股文,想看看一线项目里真实的架构决策和踩坑记录,欢迎来我的公众号「Rain的Java大神之路」坐坐。

在这里,我会把每一次技术复盘、每一个项目的设计源码,毫无保留地分享给你。

关注后回复「源码」,我为你准备了几份我亲手注释的高并发项目核心源码,希望能帮你少走一些弯路。

🔥技术这条路很酷,我们结伴同行。

🚀Keep Coding, Keep Loving ------ 与所有 Java 同路人共勉。

相关推荐
添砖java_85719 小时前
基于RabbitMQ实现的轻量队列测试报告
分布式·rabbitmq
Rain的Java大神实战圈1 天前
高并发下如何防止商品超卖少卖
经验分享·redis·高并发·架构设计·电商·面试经验
GuangdongTong1 天前
基于RabbitMQ实现延迟任务
后端·rabbitmq
doiito1 天前
【Agent Harness】Gliding Horse 整体架构拼图:当 AI Agent 有了自己的操作系统
ai·rust·架构设计·ai agent
梦想的颜色2 天前
【Docker部署插件】:使用 Docker 部署生产级 Kafka 完整版教程
安全·docker·中间件·kafka·消息队列·docker-compose·后端开发
SRETalk2 天前
高并发通知系统设计笔记:如何安全发送百万级/亿级推送?
高并发
数据库小学妹2 天前
数据库架构入门到进阶:单机、主从、分库分表到分布式的选型实战
数据仓库·分布式数据库·数据治理·数据库架构·架构设计·数据分层
doiito2 天前
Gliding Horse 时间感知系统:让 AI Agent 真正“感知“时间流逝
ai·rust·架构设计·系统设计·ai agent
淡然的煎蛋2 天前
开始使用RabbitMQ
分布式·rabbitmq·ruby