You need to enable JavaScript to run this app. Spring Boot基于Redis的电商PriceInfo多条件查询与Key设计
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot基于Redis的电商PriceInfo多条件查询与Key设计

适配双查询需求的Redis Key结构设计

你当前实现的两个硬伤

  • 全局Hash的Field直接用ProductId标识,同个商品在不同供应商的PriceInfo存储时会互相覆盖,根本保留不下多渠道的同商品价格数据
  • 没有建立供应商维度的索引,要查某个供应商的全量商品数据必须遍历全库所有Hash条目,数据量稍大就会出现性能瓶颈

核心设计思路

采用独立实体存储+双维度冗余索引的拆分结构,完全匹配两类查询的性能要求,同时尽量降低冗余存储的内存占用:

1. 单条价格数据的实体存储

每条PriceInfo的唯一标识是ProductId + Provider的组合,实体Key的统一命名规范为:
pricing:info:{productId}:{provider}

  • Key的Value直接存序列化完成的PriceInfo POJO,不管是JSON序列化、Spring Redis默认的对象序列化格式都兼容
  • 举个例子:Ebay渠道的iphone价格,对应的实体Key就是pricing:info:iphone:Ebay,Value就是这条价格记录的完整POJO

2. 单商品全渠道查询的索引结构

为了支持「查询单个商品在所有供应商的价格」的需求,给每个商品建独立的Hash索引,命名规范为:
pricing:index:product:{productId}

  • Hash的Field是具体的供应商ID(对应PriceInfo的Provider字段值)
  • Hash的Value是对应实体存储的Key引用(只存上面pricing:info:开头的实体Key路径,不存完整POJO,节省内存)
  • 举个例子:iphone的商品索引Key是pricing:index:product:iphone,Hash内会有3个Field:Amazon、Ebay、Alibaba,每个Field的值分别对应三个渠道的实体Key

3. 单供应商全量商品查询的索引结构

为了支持「查询指定供应商的全量商品价格」的需求,给每个供应商建独立的Hash索引,命名规范为:
pricing:index:provider:{providerId}

  • Hash的Field是具体的商品ID(对应PriceInfo的ProductId字段值)
  • Hash的Value同样是对应实体存储的Key引用,不存完整POJO
  • 举个例子:Ebay的供应商索引Key是pricing:index:provider:Ebay,Hash内会有2个Field:iphone、GalaxyX2,每个Field的值分别对应两个商品在Ebay的实体Key

落地代码逻辑

改写save方法

原有的save逻辑需要同步写入三个位置,保证数据一致性:

public void save(PricingInfo pricingInfo) {
    // 生成当前数据对应的实体Key
    String entityKey = String.format("pricing:info:%s:%s", 
        pricingInfo.getProductId(), pricingInfo.getProvider());
    // 存储实体POJO
    redisTemplate.opsForValue().set(entityKey, pricingInfo);
    // 写入商品维度的索引
    String productIndexKey = String.format("pricing:index:product:%s", 
        pricingInfo.getProductId());
    redisTemplate.opsForHash().put(productIndexKey, 
        pricingInfo.getProvider(), entityKey);
    // 写入供应商维度的索引
    String providerIndexKey = String.format("pricing:index:provider:%s", 
        pricingInfo.getProvider());
    redisTemplate.opsForHash().put(providerIndexKey, 
        pricingInfo.getProductId(), entityKey);
}

重复save同个ProductId+Provider的组合时,直接覆盖实体和对应的两个索引条目即可,不会产生脏数据。

对应查询实现

  • 查询单商品全渠道价格:比如查iphone的价格,直接对Keypricing:index:product:iphone执行hgetall拿到所有实体Key列表,再用mget批量取出对应的PriceInfo实体,直接组装返回结果即可,全程不需要扫描全库,耗时仅和返回的数据量正相关
  • 查询单供应商全量商品价格:比如查Ebay的商品价格,直接对Keypricing:index:provider:Ebay执行hgetall拿到所有实体Key列表,同样用mget批量取实体返回即可

落地注意细节

  • 所有Key统一带pricing:前缀,后续做批量删除、过期时间配置、线上Key排查时会省很多事
  • 给实体和对应的索引设置统一的过期时间(比如价格数据只保留30天),避免长期残留无效脏Key
  • 删除单条价格数据时,必须同步清理三个位置:实体Key、对应商品索引下的Field、对应供应商索引下的Field,防止出现索引指向不存在实体的异常
  • 不要在索引Hash里存完整的PriceInfo对象,只存实体Key的引用,商品量、供应商规模越大,这种设计的内存节约效果越明显

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 17:57:04