Elasticsearch 与 库存服务联动的完整方案

在电商系统中,库存实时扣减与 Elasticsearch 搜索的联动 是一个典型的 高并发、强一致性挑战场景。用户在搜索或下单时,必须看到准确的库存状态(如“有货”、“仅剩 3 件”),否则可能导致:

  • 超卖(库存扣成负数)
  • 用户体验差(显示有货,下单时提示无库存)

本文将为你设计一套 Elasticsearch 与 库存服务联动的完整方案,确保搜索结果中的库存信息实时、准确、高效,同时兼顾性能与一致性。


一、核心挑战

问题说明
数据延迟ES 是近实时(NRT),默认 1s 刷新,无法满足秒杀等场景
并发更新冲突高并发扣减库存时,ES 文档版本冲突
一致性保障如何保证 MySQL、Redis、ES 库存数据一致
性能瓶颈频繁更新 ES 文档影响写入吞吐

二、系统架构设计

我们采用 “缓存 + 搜索分离” + “事件驱动” 的架构:

+----------------+     +------------------+     +-----------------------+
|   用户下单     | --> |  库存服务(Redis) | --> |  Kafka / RabbitMQ     |
+----------------+     +--------+---------+     +-----------+-----------+
                                |                           |
                                v                           v
                   +----------------------+    +--------------------------+
                   | MySQL(持久化库存)   |    | Elasticsearch(搜索展示)|
                   +----------------------+    +--------------------------+

核心组件职责:

组件职责
Redis库存扣减主库(高性能、原子操作)
MySQL持久化库存,用于对账和恢复
Kafka异步通知库存变更事件
Elasticsearch展示库存状态,支持搜索过滤
库存服务控制扣减逻辑(如预扣、回滚)

三、库存扣减流程(以“下单”为例)

UserOrderServiceStockServiceRedisKafkaESobject"提交订单(SKU=1001, 数量=1)"扣减库存(decr stock)DECR stock_10012(剩余2)" send(stock_updated, sku_id=1001, stock=2)"消费事件,更新 ES 文档refresh,搜索可见success下单成功-1fail库存不足alt[扣减成功][扣减失败(库存不足)]UserOrderServiceStockServiceRedisKafkaESobject

四、Elasticsearch 索引设计(SKU 级库存字段)

sku-catalog-* 索引中增加库存相关字段:

PUT /sku-catalog-2024-10
{
  "mappings": {
    "properties": {
      "id": { "type": "keyword" },
      "title": { "type": "text" },
      "price": { "type": "scaled_float", "scaling_factor": 100 },
      "stock": { 
        "type": "integer",
        "doc_values": true  // 支持聚合与排序
      },
      "stock_status": { 
        "type": "keyword"  // in_stock, low_stock, out_of_stock
      },
      "sales_count": { "type": "long" },
      "version": { "type": "long" }  // 与 Redis 版本同步,防并发冲突
    }
  }
}

stock_status 用于前端展示和搜索过滤(如“仅显示有货”)。


五、库存更新策略(如何同步到 ES)

方案一:事件驱动 + 消费者更新(推荐)

1. 库存服务发布事件
{
  "event": "stock.updated",
  "sku_id": "1001",
  "stock": 2,
  "version": 12345,
  "timestamp": "2024-06-01T10:00:00Z"
}
2. Elasticsearch 消费者(Logstash / Flink / 自研服务)更新 ES
POST /sku-write/_update/sku_1001
{
  "doc": {
    "stock": 2,
    "stock_status": "in_stock",
    "version": 12345
  }
}
3. 乐观锁防冲突(可选)

使用 version 字段避免旧事件覆盖新数据:

POST /sku-write/_update/sku_1001?if_seq_no=100&if_primary_term=2

或在脚本中判断:

"script": {
  "source": """
    if (ctx._source.version <= params.event_version) {
      ctx._source.stock = params.stock;
      ctx._source.version = params.event_version;
      ctx._source.stock_status = params.stock > 0 ? 'in_stock' : 'out_of_stock';
    }
  """,
  "params": {
    "stock": 2,
    "event_version": 12345
  }
}

方案二:双写(不推荐)

应用层同时更新 Redis 和 ES:

redis.decr("stock:1001");
esClient.update(...);

❌ 缺点:ES 写入失败会导致数据不一致,且无法保证原子性。


方案三:定时补偿(兜底)

  • 每日夜间全量同步 MySQL → ES 库存。
  • 监控 ES 与 Redis 库存差异,告警或自动修复。

六、搜索查询中的库存过滤

1. 搜索时过滤无库存 SKU

GET /sku-read/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "iPhone" } }
      ],
      "filter": [
        { "term": { "stock_status": "in_stock" } }
      ]
    }
  }
}

2. 聚合中排除售罄 SKU

"aggs": {
  "brands": {
    "terms": {
      "field": "brand",
      "include": {
        "partition": 0,
        "num_partitions": 1
      }
    },
    "filter": { "term": { "stock_status": "in_stock" } }
  }
}

七、性能优化与高并发应对

1. 减少 ES 写入频率

  • 批量更新:消费者每 100ms 批量提交 bulk 请求。
  • 限流降级:库存更新高峰期,允许短暂延迟(如 1s 内)。

2. 缓存库存状态(可选)

  • 前端或网关缓存 stock_status,TTL=1s。
  • 避免高频查询 ES。

3. 使用 source 过滤减少传输

GET /sku-read/sku_1001?_source=stock,stock_status

八、一致性保障策略

策略说明
Redis 为唯一 truth source所有扣减以 Redis 为准
Kafka 持久化事件即使消费者宕机,重启后可重放
幂等消费消费者根据 event_id(sku_id, version) 去重
监控告警监控 Redis vs ES 库存差异 > 5% 时告警
对账服务每日比对 MySQL、Redis、ES 库存,自动修复

九、特殊场景处理

1. 秒杀场景(极高并发)

  • 预扣库存:下单前先调用 decr,成功再创建订单。
  • 延迟同步 ES:允许 ES 库存延迟 1~2 秒更新,但下单页调用 Redis 实时查询。
  • 前端降级:搜索页显示“有货”,点击后调用库存服务确认。

2. 库存回滚(订单取消)

  • 订单取消时,库存服务发送 stock.incr 事件。
  • 消费者更新 ES stock += 1

3. 超卖保护

  • Redis 扣减使用原子操作 DECR,负数则失败。
  • 设置最大重试次数,避免死循环。

十、总结:库存联动架构核心原则

原则说明
Redis 为库存权威源所有扣减在此执行
ES 仅为展示层数据可短暂延迟,但不能反向更新 Redis
事件驱动异步同步解耦库存服务与搜索系统
幂等 + 版本控制防止数据错乱
监控 + 对账兜底保障最终一致性

十一、扩展建议

场景建议方案
库存分片(如区域仓)在 ES 中增加 warehouse_id 字段,支持就近配送搜索
预售库存增加 pre_sale_stock 字段,与现货分离
虚拟库存支持超卖,但需风控拦截
搜索结果标注“仅剩 X 件”script_fields 中动态计算并高亮
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值