Caffeine是JVM级别缓存,底层数据存储采用ConcurrentHashMap。
Caffeine 使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。
缓存的淘汰策略是为了预测哪些数据在短期内最可能被再次用到,从而提升缓存的命中率。LRU由于实现简单、高效的运行时表现以及在常规的使用场景下有不错的命中率,或许是目前最佳的实现途径。但 LRU 通过历史数据来预测未来是局限的,它会认为最后到来的数据是最可能被再次访问的,从而给与它最高的优先级。这样就意味着淘汰真正热点数据,为了解决这个问题业界运用一些数据结构上的改进巧妙的解决这个问题。
引入依赖
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
使用Caffeine
package cloud.boot.caffeine;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
public class CaffeineUtil {
private static Logger logger = LoggerFactory.getLogger(CaffeineUtil.class);
/**
* 创建 Cache
*/
static Cache<String, Object> cache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS) // 如果10秒内没有读写,那么过期
.refreshAfterWrite(null) // 刷新缓存
.maximumSize(2000)// 最大 2000,超过 2000 驱逐数据
.recordStats() // 开启统计
.evictionListener((key, value, cause) -> { // 添加 eviction 监听器
logger.info("key {} - value {} is evict", key, value);
}).removalListener((key, value, cause) -> { // 添加 removal 监听器
logger.info("key {} - value {} is remove", key, value);
}).build();
public static void put(String key, Object value) {
cache.put(key, value);
}
public static Object get(String key) {
return cache.getIfPresent(key);
}
/**
* 定时打印统计信息
*/
public static void printStats() {
new Thread(()->{
while(true) {
logger.info("命中率="+cache.stats().hitRate());
logger.info("驱逐数量=" + cache.stats().evictionCount());
logger.info("命中数量=" + cache.stats().hitCount());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) throws InterruptedException {
printStats();
CaffeineUtil.put("name", "asdfasdfasfd");
CaffeineUtil.put("name2", "asdfasdfasfd2");
for(int i=0;i<3000;i++) {
CaffeineUtil.put("name"+i, "asdfasdfasfd"+i);
}
// 删除缓存
cache.invalidate("name");
// 命中数量/请求数量
logger.info((String) CaffeineUtil.get("name2"));
logger.info((String) CaffeineUtil.get("name3"));
logger.info((String) CaffeineUtil.get("name3"));
Thread.sleep(1000 * 5);
logger.info((String) CaffeineUtil.get("name"));
Thread.sleep(1000 * 11);
// 缓存过期,返回 null
logger.info((String) CaffeineUtil.get("name"));
while(true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}