我的目标是将数据内存缓存60秒。从缓存中再次读取该条目后,我想将其从缓存中删除(仅允许单次读取)。
如果这60年代同时到期,并且该条目在高速缓存中仍然可用,那么我想在该条目的后面写入数据库。
是否有任何现有的技术/弹簧/ Apache框架已经提供了这种缓存? (旁注:对于这样一个简单的用例,我不想使用诸如redis,ehcache等复杂的库。)
如果手动设置,我将执行以下操作。但是也许还有更好的选择?
@Service
public class WriteBehindCache {
static class ObjectEntry {
Object data;
LocalDateTime timestamp;
public ObjectEntry(Object data) {
this.data = data;
timestamp = LocalDateTime.now();
}
}
Map<String, ObjectEntry> cache = new ConcurrentHashMap<>();
//batch every minute
@Scheduled(fixedRate = 60000)
public void writeBehind() {
LocalDateTime now = LocalDateTime.now();
List<ObjectEntry> outdated = cache.values()
.filter(entry -> entry.getValue().timestamp.plusSeconds(60).isBefore(now))
.collect(Collectors.toList());
databaseService.persist(outdated);
cache.removeAll(outdated); //pseudocode
}
//always keep most recent entry
public void add(String key, Object data) {
cache.put(key, new ObjectEntry(data));
}
//fallback lookup to database if cache is empty
public Object get(String key) {
ObjectEntry entry = cache.remove(key);
if (entry == null) {
entry = databaseService.query(key);
if (entry != null) databaseService.remove(entry);
}
return entry;
}
}
答案 0 :(得分:1)
您的解决方案有两个问题:
是否有任何现有的技术/弹簧/ Apache框架已经提供了这种缓存? (旁注:对于这样一个简单的用例,我不想使用诸如redis,ehcache等复杂的库。)
我认为您可以基于ConcurrentHashMap
解决并发问题。但是我不知道超时的优雅方法。不过,可能的解决方案是使用缓存库。我想提供一个基于cache2k的示例,该示例并不繁琐(大约40万个jar),并且还有其他很好的用例。另外,Spring缓存抽象也得到了很好的支持。
public static class WriteBehindCache {
Cache<String, Object> cache = Cache2kBuilder.of(String.class, Object.class)
.addListener((CacheEntryExpiredListener<String, Object>) (cache, entry)
-> persist(entry.getKey(), entry.getValue()))
.expireAfterWrite(60, TimeUnit.SECONDS)
.build();
public void add(String key, Object data) {
cache.put(key, data);
}
public Object get(String key) {
return cache.invoke(key, e -> {
if (e.exists()) {
Object v = e.getValue();
e.remove();
return v;
}
return loadAndRemove(e.getKey());
});
}
// stubs
protected void persist(String key, Object value) {
}
protected Object loadAndRemove(String key) {
return null;
}
}
通过这种连接,高速缓存会阻止对一个条目的并发操作,因此可以确定一次只有一个数据库操作针对一个条目运行。
您可以使用其他缓存库以类似方式进行操作。使用JCache / JSR107 API,代码看起来几乎相同。
更“轻松”的方法是使用贾特曼(Jhalterman)的expiringmap
我个人认为,每个开发人员工具箱中都应该有一个缓存。但是,我是cache2k的作者。当然,我需要这么说。