我有以下缓存问题:
// imagine we have a method that performs a long running batch query
List<Record> fetchRecords(List<String> recordKeys)
我们希望使用单个记录密钥作为缓存密钥而不是整个列表来缓存此长时间运行操作的结果
这一结果的好处是显而易见的。也就是说,在下一次调用fetchRecords(overlappingRecordKeys)
时,长时间运行的批处理查询将仅包括先前未包含的那些键。这是我们通过测试整个List
上的平等而无法获得的好处。
这不能通过keyGenerator
拦截器解决,因为该机制只允许我们在每次调用方法时返回单个缓存键
解决此问题的最佳方法是什么?我能想到两个解决方案
创建CachingAspect
// pseudo code
@Aspect
class CachingAspect {
CacheManager mgr = ...
@Around("somePointcut")
void checkCache(ProceedingPointCut pc) {
// grab args from 'pc' which is of type List<String>
// check against 'mgr', modify the argument to exclude those records already in cache
// capture the output (i.e. new records from long-running operation), append with records retrieved from cache, return the union
}
}
与没有AOP的上述相同的想法(将代码直接放在fetchRecords()
中。这样可以增加类型安全性,但代价是优雅
哪种解决方案更好?还是有第三种优越的方法?
答案 0 :(得分:0)
关于批量操作和弹簧缓存抽象的问题经常出现。这不包括在内。
......以牺牲优雅为代价
嗯,优雅是品味的问题。追求优雅(通过AOP)和效率可能是令人头疼的问题;)
没有注释的直接解决方案,是使用缓存加载器的读取配置。例如,JCache / JSR107兼容缓存为您提供了Cache.getAll(keys)
操作,这正是您要求的。
侧节点:执行批量请求时实际发生的情况会有所不同,从缓存实现到缓存实现并不是一件容易的事。例如,如果您希望阻止每个键以避免并行多次获取一个键的值,则还需要防止死锁。