You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

下单后Solr库存实时刷新流程优化及问题排查咨询

下单后Solr库存实时刷新流程阻塞问题:排查与优化方案

一、现有实现的核心问题

  1. 订单主流程被同步阻塞:把Solr索引刷新硬塞在订单串行流程里,还用Thread.sleep(10000)循环等待索引任务完成,直接占用订单处理线程,导致整个订单流程迟迟走不到success节点,连带卡断后续依赖的业务动作(比如订单确认邮件发送)。
  2. JVM级锁的局限性:用ReentrantLock做店铺级别锁,集群部署下锁不共享,会导致多节点同时跑索引任务;单节点内所有该店铺的订单请求都会被堵在锁获取环节,直接拖垮系统吞吐量。
  3. 误用全量索引任务:调用的SolrIndexerCronJob是全量索引更新,明明只需要更新下单商品的库存,却要刷新整个索引库,执行时间长,进一步放大阻塞问题。
  4. 低效的等待逻辑:10秒间隔的轮询不仅浪费线程资源,还会让订单流程等待时间不可控,用户体验和系统性能双重受损。

二、优化方案:复用现有Action改逻辑(不用彻底重构)

核心思路是异步解耦+增量更新+分布式锁(可选),基于现有RefreshSolrAction修改即可:

1. 异步化处理,脱离订单主流程

把索引刷新从订单串行流程中剥离,提交异步任务后直接返回,不阻塞订单流程:

@Override
public Transition executeAction(OrderProcessModel orderProcessModel) throws Exception {
    if (Objects.nonNull(orderProcessModel.getOrder())) {
        final OrderModel order = orderProcessModel.getOrder();
        // 提交异步任务,直接放行订单流程
        solrRefreshAsyncTask.submit(() -> {
            try {
                refreshStockForOrderedProducts(order);
            } catch (Exception e) {
                // 只打日志,不影响主流程
                log.error("Solr库存更新失败,订单号:{}", order.getCode(), e);
            }
        });
    }
    return Transition.OK;
}

2. 改为增量更新,避免全量索引

针对订单内的商品单独更新Solr库存字段,不用跑全量CronJob:

private void refreshStockForOrderedProducts(OrderModel order) {
    BaseStoreModel store = order.getStore();
    // 遍历订单里的所有商品
    for (OrderEntryModel entry : order.getEntries()) {
        ProductModel product = entry.getProduct();
        // 获取该商品在当前店铺的最新库存
        Long currentStock = stockService.getStockLevelForProductAndStore(product, store);
        // 只更新Solr中该商品的库存字段
        solrService.updateProductStockField(product.getCode(), currentStock, store.getSolrCore());
    }
    // 根据Solr配置决定是否需要提交,建议用软提交提升性能
    solrService.softCommit(store.getSolrCore());
}

3. 分布式锁控制并发(集群场景必加)

如果是集群部署,把JVM级的ReentrantLock换成Redis分布式锁,避免多节点同时更新同一商品的库存:

private void refreshStockForOrderedProducts(OrderModel order) {
    BaseStoreModel store = order.getStore();
    for (OrderEntryModel entry : order.getEntries()) {
        ProductModel product = entry.getProduct();
        String lockKey = String.format("solr_stock_lock:%s:%s", store.getUid(), product.getCode());
        // 尝试获取锁,超时5秒,防止死锁
        if (redisLockService.tryLock(lockKey, 5, TimeUnit.SECONDS)) {
            try {
                Long currentStock = stockService.getStockLevelForProductAndStore(product, store);
                solrService.updateProductStockField(product.getCode(), currentStock, store.getSolrCore());
            } finally {
                // 必须释放锁
                redisLockService.unlock(lockKey);
            }
        }
    }
    solrService.softCommit(store.getSolrCore());
}

4. 调整订单流程配置

异步化后,不需要让refreshSolr动作阻塞订单流程,直接调整orderProcess.xml:

<action id="decrementStockOrderPlaced" bean="decrementStockOrderPlacedAction">
    <!-- 库存扣减完成后直接到success,不用等Solr更新 -->
    <transition name="OK" to="success"/>
    <transition name="NOK" to="success"/>
</action>
<!-- 如果保留refreshSolr动作,确保它是异步的,不阻塞流程 -->
<action id="refreshSolr" bean="refreshSolrAction">
    <transition name="OK" to="success"/>
    <transition name="NOK" to="success"/>
</action>

三、额外优化点

  • Solr软提交配置:把Solr的autoSoftCommit设为1秒左右,平衡实时性和性能,不用每次更新都手动提交。
  • 批量合并更新:短时间内多个订单涉及同一商品时,合并更新请求,减少Solr调用次数。
  • 监控告警:给异步更新任务加监控,统计成功/失败次数,失败率过高时触发告警。

内容的提问来源于stack exchange,提问作者iamrooovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:24:51