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

如何在Spring Boot中基于唯一Key用Redis单独缓存列表元素?

Spring Boot Redis 单元素缓存实现方案

问题背景

当前使用Spring Boot集成Redis,原方法通过@Cacheable将整个收益率列表缓存到同一个Key下。现需求改为:将列表中每个元素以fund_price:ABC:2023-01-01:2023-02-15这类唯一Key单独缓存,后续单个请求可直接复用缓存值,无需重复计算。

现有代码:

@Cacheable(value = "fundYields", key = "#colName + ':' + #code + ':' + #firstDate + ':' + #dateList")
public List<BigDecimal> calculateYields(String colName, String code, String firstDate, List<String> dateList) {
    List<BigDecimal> results = new ArrayList<>();
    for (String date : dateList) {
       // 创建NoSQL自定义聚合操作,单次数据库请求获取数据
    }
    // ..
    return results;
}

期望缓存结构:

{
  "fund_price:ABC:2023-01-01:2023-02-15": 10.5
}
{
  "fund_price:ABC:2023-01-01:2023-03-10": 12.8
}

方案说明

直接用@Cacheable实现?不行

@Cacheable是针对方法返回值整体做缓存的,原方法返回List<BigDecimal>,只能把整个列表绑定到一个Key下,无法自动拆分每个元素到独立Key。如果在循环里调用带@Cacheable的单个元素计算方法,会导致每个元素触发一次数据库查询,严重降低性能。

最优高效方案:缓存预检查+批量查询补全

核心思路是:先检查每个日期对应的缓存是否存在,只对缺失缓存的日期做批量数据库查询,再将结果逐个存入缓存,同时返回完整列表。既保证缓存复用,又保留原批量查询的性能优势。

代码实现

  1. 单个元素的缓存方法(供后续单个请求直接调用):
@Cacheable(value = "fundYields", key = "#colName + ':' + #code + ':' + #firstDate + ':' + #date")
public BigDecimal calculateSingleYield(String colName, String code, String firstDate, String date) {
    // 单个日期的计算逻辑,可复用批量查询方法
    return fetchBatchYields(colName, code, firstDate, Collections.singletonList(date)).get(date);
}
  1. 修改原批量方法,加入缓存检查与补全逻辑:
@Autowired
private CacheManager cacheManager;

public List<BigDecimal> calculateYields(String colName, String code, String firstDate, List<String> dateList) {
    List<BigDecimal> results = new ArrayList<>(Collections.nCopies(dateList.size(), null));
    List<String> missingDates = new ArrayList<>();
    Cache cache = cacheManager.getCache("fundYields");

    // 第一步:检查缓存,收集缺失日期
    for (int i = 0; i < dateList.size(); i++) {
        String date = dateList.get(i);
        String cacheKey = createKey(colName, code, firstDate, date);
        BigDecimal cachedYield = cache.get(cacheKey, BigDecimal.class);
        
        if (cachedYield != null) {
            results.set(i, cachedYield);
        } else {
            missingDates.add(date);
        }
    }

    // 第二步:批量查询缺失数据,填充缓存与结果
    if (!missingDates.isEmpty()) {
        Map<String, BigDecimal> fetchedYields = fetchBatchYields(colName, code, firstDate, missingDates);
        
        for (Map.Entry<String, BigDecimal> entry : fetchedYields.entrySet()) {
            String date = entry.getKey();
            BigDecimal yield = entry.getValue();
            String cacheKey = createKey(colName, code, firstDate, date);
            cache.put(cacheKey, yield);
            
            // 替换结果列表中对应位置的值
            int index = dateList.indexOf(date);
            if (index != -1) {
                results.set(index, yield);
            }
        }
    }

    return results;
}

// 生成统一缓存Key的辅助方法
private String createKey(String colName, String code, String firstDate, String date) {
    return String.format("%s:%s:%s:%s", colName, code, firstDate, date);
}

// 提取原有的批量数据库查询逻辑,返回日期与收益率的映射
private Map<String, BigDecimal> fetchBatchYields(String colName, String code, String firstDate, List<String> dateList) {
    Map<String, BigDecimal> yieldsMap = new HashMap<>();
    // 原NoSQL聚合操作逻辑,针对传入的dateList批量查询,将结果存入yieldsMap
    // ...
    return yieldsMap;
}

方案优势

  • 缓存复用最大化:已缓存的元素直接从Redis获取,无需重复计算/查询
  • 性能最优:仅对缺失缓存的日期执行单次批量数据库请求,避免循环查库的开销
  • 满足后续单个请求需求:后续需要单个日期的收益率时,直接调用calculateSingleYield方法即可命中缓存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:50:08