如何在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的单个元素计算方法,会导致每个元素触发一次数据库查询,严重降低性能。
最优高效方案:缓存预检查+批量查询补全
核心思路是:先检查每个日期对应的缓存是否存在,只对缺失缓存的日期做批量数据库查询,再将结果逐个存入缓存,同时返回完整列表。既保证缓存复用,又保留原批量查询的性能优势。
代码实现
- 单个元素的缓存方法(供后续单个请求直接调用):
@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); }
- 修改原批量方法,加入缓存检查与补全逻辑:
@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
相关产品推荐
相关产品推荐

