• 我的订阅
  • 科技

基于Spring Cache实现Caffeine、jimDB多级缓存实战

类别:科技 发布时间:2023-01-31 11:00:00 来源:京东云企业管家

作者: 京东零售 王震

背景

在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不同的特性提升接口性能,

内存缓存采用Caffeine缓存,利用W-TinyLFU算法获得更高的内存命中率;同时利用堆外缓存降低内存缓存大小,减少GC频率,同时也减少了网络IO带来的性能消耗;利用JimDB提升接口高可用、高并发;后期通过压测及性能调优999性能<20ms

基于Spring Cache实现Caffeine、jimDB多级缓存实战

当时由于项目工期紧张,三级缓存实现较为臃肿、业务侵入性强、可读性差,在近期场景化推荐项目中,为B端商家场景化资源投放推荐,考虑到B端流量相对C端流量较小,但需保证接口性能稳定。采用SpringCache实现caffeine、jimDB多级缓存方案,实现了低侵入性、可扩展、高可用的缓存方案,极大提升了系统稳定性,保证接口性能小于100ms;

Spring Cache实现多级缓存

多级缓存实例MultilevelCache

/**

* 分级缓存

* 基于Caffeine + jimDB 实现二级缓存

* @author wangzhen520

* @date 2022/12/9

*/

public class MultilevelCache extends AbstractValueAdaptingCache {

/**

* 缓存名称

*/

private String name;

/**

* 是否开启一级缓存

*/

private boolean enableFirstCache = true;

/**

* 一级缓存

*/

private Cache firstCache;

/**

* 二级缓存

*/

private Cache secondCache;

@Override

protected Object lookup(Object key) {

Object value;

recordCount(getUmpKey(this.getName(), UMP_GET_CACHE, UMP_ALL));

if(enableFirstCache){

//查询一级缓存

value = getWrapperValue(getForFirstCache(key));

log.info("{}#lookup getForFirstCache key={} value={}", this.getClass().getSimpleName(), key, value);

if(value != null){

return value;

}

}

value = getWrapperValue(getForSecondCache(key));

log.info("{}#lookup getForSecondCache key={} value={}", this.getClass().getSimpleName(), key, value);

//二级缓存不为空,则更新一级缓存

boolean putFirstCache = (Objects.nonNull(value) || isAllowNullValues()) && enableFirstCache;

if(putFirstCache){

recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT));

log.info("{}#lookup put firstCache key={} value={}", this.getClass().getSimpleName(), key, value);

firstCache.put(key, value);

}

return value;

}

@Override

public void put(Object key, Object value) {

if(enableFirstCache){

checkFirstCache();

firstCache.put(key, value);

}

secondCache.put(key, value);

}

/**

* 查询一级缓存

* @param key

* @return

*/

private ValueWrapper getForFirstCache(Object key){

checkFirstCache();

ValueWrapper valueWrapper = firstCache.get(key);

if(valueWrapper == null || Objects.isNull(valueWrapper.get())){

recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT));

}

return valueWrapper;

}

/**

* 查询二级缓存

* @param key

* @return

*/

private ValueWrapper getForSecondCache(Object key){

ValueWrapper valueWrapper = secondCache.get(key);

if(valueWrapper == null || Objects.isNull(valueWrapper.get())){

recordCount(getUmpKey(this.getName(), UMP_SECOND_CACHE, UMP_NO_HIT));

}

return valueWrapper;

}

private Object getWrapperValue(ValueWrapper valueWrapper){

return Optional.ofNullable(valueWrapper).map(ValueWrapper::get).orElse(null);

}

}

多级缓存管理器抽象

/**

* 多级缓存实现抽象类

* 一级缓存

* @see AbstractMultilevelCacheManager#getFirstCache(String)

* 二级缓存

* @see AbstractMultilevelCacheManager#getSecondCache(String)

* @author wangzhen520

* @date 2022/12/9

*/

publicabstractclassAbstractMultilevelCacheManagerimplementsCacheManager {

private final ConcurrentMap cacheMap = newConcurrentHashMap<>(16);

/**

* 是否动态生成

* @see MultilevelCache

*/

protected boolean dynamic = true;

/**

* 默认开启一级缓存

*/

protected boolean enableFirstCache = true;

/**

* 是否允许空值

*/

protected boolean allowNullValues = true;

/**

* ump监控前缀 不设置不开启监控

*/

privateString umpKeyPrefix;

protectedMultilevelCachecreateMultilevelCache(String name) {

Assert.hasLength(name, "createMultilevelCache name is not null");

MultilevelCache multilevelCache = newMultilevelCache(allowNullValues);

multilevelCache.setName(name);

multilevelCache.setUmpKeyPrefix(this.umpKeyPrefix);

multilevelCache.setEnableFirstCache(this.enableFirstCache);

multilevelCache.setFirstCache(getFirstCache(name));

multilevelCache.setSecondCache(getSecondCache(name));

return multilevelCache;

}

@Override

publicCachegetCache(String name) {

MultilevelCache cache = this.cacheMap.get(name);

if (cache == null && dynamic) {

synchronized (this.cacheMap) {

cache = this.cacheMap.get(name);

if (cache == null) {

cache = createMultilevelCache(name);

this.cacheMap.put(name, cache);

}

return cache;

}

}

return cache;

}

@Override

publicCollection getCacheNames() {

returnCollections.unmodifiableSet(this.cacheMap.keySet());

}

/**

* 一级缓存

* @param name

* @return

*/

protectedabstractCachegetFirstCache(String name);

/**

* 二级缓存

* @param name

* @return

*/

protectedabstractCachegetSecondCache(String name);

public boolean isDynamic() {

return dynamic;

}

public void setDynamic(boolean dynamic) {

this.dynamic = dynamic;

}

public boolean isEnableFirstCache() {

return enableFirstCache;

}

public void setEnableFirstCache(boolean enableFirstCache) {

this.enableFirstCache = enableFirstCache;

}

publicStringgetUmpKeyPrefix() {

return umpKeyPrefix;

}

public void setUmpKeyPrefix(String umpKeyPrefix) {

this.umpKeyPrefix = umpKeyPrefix;

}

}

基于jimDB Caffiene缓存实现多级缓存管理器

/**

* 二级缓存实现

* caffeine + jimDB 二级缓存

* @author wangzhen520

* @date 2022/12/9

*/

publicclassCaffeineJimMultilevelCacheManagerextendsAbstractMultilevelCacheManager {

private CaffeineCacheManager caffeineCacheManager;

private JimCacheManager jimCacheManager;

public CaffeineJimMultilevelCacheManager(CaffeineCacheManager caffeineCacheManager, JimCacheManager jimCacheManager) {

this.caffeineCacheManager = caffeineCacheManager;

this.jimCacheManager = jimCacheManager;

caffeineCacheManager.setAllowNullValues(this.allowNullValues);

}

/**

* 一级缓存实现

* 基于caffeine实现

* @see org.springframework.cache.caffeine.CaffeineCache

* @param name

* @return

*/

@Override

protected Cache getFirstCache(String name) {

if(!isEnableFirstCache()){

return null;

}

return caffeineCacheManager.getCache(name);

}

/**

* 二级缓存基于jimDB实现

* @see com.jd.jim.cli.springcache.JimStringCache

* @param name

* @return

*/

@Override

protected Cache getSecondCache(String name) {

return jimCacheManager.getCache(name);

}

}

缓存配置

/**

* @author wangzhen520

* @date 2022/12/9

*/

@Configuration

@EnableCaching

public class CacheConfiguration {

/**

* 基于caffeine + JimDB 多级缓存Manager

* @param firstCacheManager

* @param secondCacheManager

* @return

*/

@Primary

@Bean(name = "caffeineJimCacheManager")

public CacheManager multilevelCacheManager(@Param("firstCacheManager") CaffeineCacheManager firstCacheManager,

@Param("secondCacheManager") JimCacheManager secondCacheManager){

CaffeineJimMultilevelCacheManagercacheManager = newCaffeineJimMultilevelCacheManager(firstCacheManager, secondCacheManager);

cacheManager.setUmpKeyPrefix(String.format("%s.%s", UmpConstants.Key.PREFIX, UmpConstants.SYSTEM_NAME));

cacheManager.setEnableFirstCache(true);

cacheManager.setDynamic(true);

returncacheManager;

}

/**

* 一级缓存Manager

* @return

*/

@Bean(name = "firstCacheManager")

publicCaffeineCacheManagerfirstCacheManager(){

CaffeineCacheManagerfirstCacheManager = newCaffeineCacheManager();

firstCacheManager.setCaffeine(Caffeine.newBuilder()

.initialCapacity(firstCacheInitialCapacity)

.maximumSize(firstCacheMaximumSize)

.expireAfterWrite(Duration.ofSeconds(firstCacheDurationSeconds)));

firstCacheManager.setAllowNullValues(true);

returnfirstCacheManager;

}

/**

* 初始化二级缓存Manager

* @param jimClientLF

* @return

*/

@Bean(name = "secondCacheManager")

publicJimCacheManagersecondCacheManager(@Param("jimClientLF") Cluster jimClientLF){

JimDbCachejimDbCache = newJimDbCache<>();

jimDbCache.setJimClient(jimClientLF);

jimDbCache.setKeyPrefix(MultilevelCacheConstants.SERVICE_RULE_MATCH_CACHE);

jimDbCache.setEntryTimeout(secondCacheExpireSeconds);

jimDbCache.setValueSerializer(new JsonStringSerializer(ServiceRuleMatchResult.class));

JimCacheManagersecondCacheManager = newJimCacheManager();

secondCacheManager.setCaches(Arrays.asList(jimDbCache));

returnsecondCacheManager;

}

接口性能压测

压测环境

廊坊4C8G * 3

压测结果

1、50并发时,未开启缓存,压测5min,TP99: 67ms,TP999: 223ms,TPS:2072.39笔/秒,此时服务引擎cpu利用率40%左右;订购履约cpu利用率70%左右,磁盘使用率4min后被打满;

2、50并发时,开启二级缓存,压测10min,TP99: 33ms,TP999: 38ms,TPS:28521.18.笔/秒,此时服务引擎cpu利用率90%左右,订购履约cpu利用率10%左右,磁盘使用率3%左右;

缓存命中分析

总调用次数:1840486/min 一级缓存命中:1822820 /min 二级缓存命中:14454/min

一级缓存命中率:99.04%

二级缓存命中率:81.81%

压测数据

未开启缓存

基于Spring Cache实现Caffeine、jimDB多级缓存实战

开启多级缓存

基于Spring Cache实现Caffeine、jimDB多级缓存实战

监控数据

未开启缓存

下游应用由于4分钟后磁盘打满,性能到达瓶颈

接口UMP

基于Spring Cache实现Caffeine、jimDB多级缓存实战

服务引擎系统

基于Spring Cache实现Caffeine、jimDB多级缓存实战

订购履约系统

基于Spring Cache实现Caffeine、jimDB多级缓存实战

开启缓存

上游系统CPU利用率90%左右,下游系统调用量明显减少,CPU利用率仅10%左右

接口UMP

基于Spring Cache实现Caffeine、jimDB多级缓存实战

服务引擎系统

基于Spring Cache实现Caffeine、jimDB多级缓存实战

订购履约系统:

基于Spring Cache实现Caffeine、jimDB多级缓存实战

返回搜狐,查看更多

责任编辑:

以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。

快照生成时间:2023-01-31 12:45:17

本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。

信息原文地址:

可超高频,可降延迟!金百达银爵DDR5内存ZEN4平台超频实战!
...EL”,工艺为5NM,规格为8核心16线程,频率4.5-5.4GHz,二级缓存8MB,三级缓存32MB,热设计功耗105W
2022-12-21 06:00
谁是电竞网游最强U?锐龙9 7950X3D制霸全场
采用AMD3DV-Cache缓存技术的锐龙7000X3D系列终于上市,其中的旗舰锐龙97950X3D以当前最强的综合游戏性能超越了竞品酷睿i913900KS
2023-03-16 11:46:00
intel造了一颗“算力存力核弹”
...模扩展方面收益的任务,以追求更高的能效、更高的机架利用率。而现在发布的至强6性能核更适合大数据、建模仿真等计算密集型和人工智能任务,为高性能优化,单颗处理器的功耗直飚500W
2024-09-27 11:45:00
Lunar Lake工程样品性能参数曝光
...超线程技术,为8核心8线程外,非常值得注意的就是其L1缓存达到了832KB,L2缓存的容量更是比L3缓存还要大,达到了14MB
2024-02-20 10:02:00
AMD锐龙9 7000X3D正式登场:两大神级优化
...果游戏的同时还在执行其他任务,比如直播什么的,线程利用率足够高,超过了一个CCX的承载能力,就会自动启动休眠的第二个CCX,兼顾多任务性能。驱动安装成功后,就可以在设备管理的
2023-03-01 20:54:00
瑞昱展示其PCIe 5.0 SSD主控 支持缓存旗舰性能
.../s的闪存接口,支持4KLDPCECC纠错,可采用DDR4、LPDDR3/4x作为缓存,性能方面也是相当顶级的,连续读取速度高达14000MB/s
2024-06-16 12:27:00
西部数据红盘wdredsn700nvme硬盘展示
...,在使用了2块最新的8TB红盘的同时增加了SN700NVMeSSD作为缓存盘已提升数据访问的命中率,具体相关操作文章中会有过程
2023-01-28 22:49:00
旧电脑加装固态硬盘后,系统运行依旧卡顿的原因是什么
...足,更不用说其他的了。在加装固态时,我们要分清楚:缓存速度和介质速度!很多固态SSD测评文章中,都会提及跑分测试速度达到了多少,这个数值一般是指缓存速度。而SSD的整体速度取
2023-01-10 11:05:00
华硕13600k配置单推荐
...5-12490F拥有6核心12线程,CPU主频3GHz,最高睿频4.5GHz,三级缓存20MB,PBP功耗65W,支持DDR5内存
2023-11-06 09:16:00
更多关于科技的资讯: