发布时间:2025-11-18
浏览次数:0
大家对于其必然所知有所体会,它身为具备高性能的 Java 缓存库,被大量运用在纷杂多样的各种 Java 框架情形里,也被大量运用在中间件范畴之中,还被大量运用在数据库访问层等相关场景之内,绝大多数的高并发项目都采用了本地缓存这一方式,以此来提高响应的速度,进而降低数据库所承受的压力,且进一步优化高并发性能表现。接下来我们要深入地去了解一番......
前言
针对高并发系统而言,其中缓存是能够提升性能的核心组件当中的一个。它身为一款具备高性能的Java缓存库,依靠其淘汰算法以及卓越的并发性能,从而成为众多系统的首选。在本文里,将会深入去解析其的设计原理、核心架构、关键API,并且借助一个高并发场景的实战案例,来展示其应用办法。但需要注意的是,这里的“其”指代不明,应替换为具体所指的缓存库名称,不然表达易产生歧义。
C 一、其核心原理的淘汰策略是,采用一种算法,该算法结合了 LRU(最近最少使用)以及 LFU(最不经常使用)的优点 ,#技术分享:滑动窗口分区方面,缓存被划分成一个主区域(用于保护高频数据)和一个窗口区域(用来接纳新数据),以此避免突发流量对缓存造成污染。频率素描(Count - Min )是,能以极低的内存开销统计数据访问频率,进而替代传统 LFU 的哈希计数。高性能并发设计中的分段锁机制是,写操作进行分段加锁,从而减少线程竞争。 无锁读优化: 通过 实现并发读的高性能。
关键数据结构设计
class CountMinSketch {
有一个二维长整型数组,名为matrix,它被创建,其长度为4,且每一个元素都是一个长整型一维数组的引用 。
void increment(key) {
for (int i = 0; i < 4; i++) {
matrix[i][hash(i, key)]++;
}
}
}
class Node这句话看起来像是一段代码声明呀,如果按照要求改写会变得很奇怪且失去代码原本的表达意义呢,请确认下是否有更合适的内容让我改写🧐 不过硬要改写的话:有一个K类型的key,还有一个V类型的value,存在一个易失性。 prev, next; }
下面我给出读请求和写请求两种模块内部工作交互
用户进行 cache.get("key") 的调用,去查看,命中了吗,若是命中,则返回数据,并且频率加 1,若继续未命中,那么调用 .load("A")去加载数据,数据加载完成之后,写入,更新 Count - Min(频率加 1),返回数据,调用 cache.put(key, value),锁获取,依据 key.() 计算出对应的锁(默认 16 个分段),锁之内进行操作。
第一步:查验看看此Key是不是已经存在着,要是存在的话,那就将旧有的值给覆盖掉,要是不存在的话,那就进入到淘汰的逻辑当中,是这样的情况 。
步骤2:
对比新的数据与其中最为陈旧的数据的频率素描计数,新的数据频率更高,那么淘汰旧的数据,让新的数据进入Main区,旧的数据频率更高,那么丢弃新的数据。
4.
记录访问时间戳(LRU 逻辑)
释放锁: 完成写入
下面给出伪代码
if (window.isFull()) {
新建了一个名为Candidate的对象,将其命名为newEntry,该对象是通过使用key和value创建而成的。
mainRegion.admit(newEntry);
window.evict(victim);
}
}
C 二、 的架构分层
| 模块 | 功能描述 | | ---
对于Cache而言,其定义缓存的核心API,比如get、put等等。它要实现算法,去管理缓存淘汰逻辑。并且通过分段锁以及CAS操作来保证线程安全。 对于Data来说,它基于存储缓存条目,具备支持高效查找的特性。
C 三、 重点方法详解1. 初始化方法:.()
Cache cache = Caffeine.newBuilder()
.maximumSize(10_000)
这段似乎不是一个完成语句呢,请确认下完整需改写的内容。单从这部分来看,可改写为:使其在写入之后经过十分钟就过期,这里的十分钟使用时间单位为分钟。
设置在有读取操作后、五 以时间单位分钟计便过期 经历这段设定时长就会过期。
进行在写入之后的刷新操作,时间为1分钟,时间单位为国标时间单位中的分钟 。
.weakKeys()
.weakValues()
.recordStats()
消除监听器,(针对)所给定的(键、值以及原因),(生成这样的陈述功能) ->。
这是一段代码语句,直接改写会破坏代码结构和功能,因此无法按照要求进行改写。你可以提供其他合适的文本内容让我进行改写 。
关键参数:
撰写:具备严格的一致性呢,是较为适配配置类数据的哟。:拥有更高的命中率呀,是比较适合热点数据的呢。
2. 数据写入:put(K key, V value)
cache 将 “key” 对应的内容放置上,放置的是一个全新的对象,该操作发生储存行为 ,进行这样内容为新。
底层逻辑:
进行 key.() 的计算,以此判定其所属情况,这里存在默认16个分段的设定。接着获取分段锁(),通过这种方式来确保线程处于安全状态。然后执行写入操作:要是 Key 已然存在,那么就直接进行覆盖。要是 Key 并不存在,会进入后续流程,即淘汰流程:
if (window.isFull()) {
倘若,频率草图相比于新数据与最旧数据而言,进行比较时其大于零 ,条件为真 若频率草图就新数据和最旧数据来做出比较,此时此频率草图相应比值大于零 ,即为真 要是频率草图针对新。
mainRegion.admit(newData);
}
}
使Count - Min频率统计予以更新,此涉及无锁CAS操作,将分段锁进行释放。还要进行数据读取,其形式为get(K key, ) 。
对象值等于缓存获取,通过键“key”,以及一个函数,该函数接受键作为参数并从数据库加载数据 。
执行流程:
无锁读:尝试从 获取值(get() 为原子操作)。
2.
发起同步调用,借助.apply(key)来加载数据,强调同一Key并发之时只会加载一回,而后将其写入缓存,并且对频率素描进行更新。
缓存命中:更新 LRU 访问时间戳,返回缓存值。
高并发优化: 使用 避免阻塞:
AsyncLoadingCache变为异步缓存的对象等于使用咖啡因建立器创建新的构建器 ,句号使用中文的话,语句会读起来更顺一些,但是按你要求。
通过构建异步操作,针对键执行从数据库加载数据的操作,将操作委托给传入键为参数的回调函数来实现,该函数接收键,并负责具体的从数据库加载。

CompletableFuture
4. 数据淘汰:(key) 与自动淘汰
cache.invalidate("key");
cache.invalidateAll();
自动淘汰触发条件:
按照大小方面所处情况flinto for sketch,会引发淘汰赛,按照时间方面相关情形,后台线程在一定的周期里会进行清理,依据引用方面的状况,依靠垃圾回收机制来作用 。
拆除监听器,此监听器会在接收到键、值以及原因时执行回调,回调内容为一个箭头函数,该函数接受键、值以及原因作为参数。
listener通知其过期,针对总线标识,把键转换成对应的字符串形式 。
})
5.统计与监控:()
()启用缓存统计功能,记录以下核心指标:
{
; // 缓存命中次数
; // 缓存未命中次数
; // 成功加载新值的次数
; // 加载失败次数
; // 总加载耗时(纳秒)
; // 因容量或过期导致的淘汰总数
; // 淘汰条目的总权重(仅在使用时有效)
在缓存上调用统计方法所得到的数据对象,被赋值给了名为stats的变量,这个变量的数据类型是CacheStats ,而获取这个数据。
System.out.printf ("命中率: %.1f%%", "加载次数: %d", "淘汰数: %d") ,。
stats.hitRate() * 100,
stats.loadCount(),
stats.evictionCount());
关键指标:
6. 异步方法:(key)
cache.refresh("key");
和 的差异在于::是马上就失效,请求的时候得等待新的值进行加载。:是在后台加载,旧时的值能够继续提供服务(会更加平滑)。
源码性能优化技巧
频率素描也就是(Count-Min ):是运用 4 个 long 矩阵来统计频率的,其误差率经过分段锁优化,此优化默认是 16 个,会使得并发写性能距离 O(1) 这点接近。它还具备无锁读路径:所有读操作是完全不存在锁的(要对变量进行相应操作)。
四、高并发场景实战:商品信息缓存
环境状况:存在着每秒可达万级数量的读请求,这些读请求涵盖商品查询以及架构查询等方面,并且有着这样的要求,即要达成99.9%的缓存命中率。需要做到防止缓存击穿以及雪崩的情况发生,同时还要支持平滑过期刷新。
导入,来自,那个,被称为,本,马内斯,的,人,所拥有的,名为,咖啡因,的,缓存,相关的,一系列,内容,标点符号。
import, java里面的, util这个包里面的, concurrent这个子包,里面的所有内容 。
引入,用于 Java 的,包含线程安全方法布尔值的,原子性包装类,AtomicBoolean 。
cache等于Caffeine的newBuilder实例,该实例设置最大容量为10000,在此之后调用基于写入时间的过期策略,时长为30分钟,接着调用基于写入时间的刷新策略,刷新时长为5分钟,之后调用executor方法,参数为通过Executors创建的固定大小线程池,线程数量为4,再之后调用recordStats方法登记统计信息,最后调用build方法并传入ProductLoader实例 。
**私有的**,**静态的**,**最终的**(常量修饰符),**并发映射**。加载锁被创建为一个新的,具有并发特性的哈希映射 ,句号。<>();
私有的静态类,其名为ProductLoader,它实现了CacheLoader接口 。重写之后,公共的产品加载部分使用长格式,在重写时,通过抛出异常,从数据库中获取产品ID对应的产品详情,返回获取到的产品详情,并且重写时使用覆盖的方式进行操作 。
重写为方法覆盖@Override,此方法名为public Product reload,参数为Long类型的productId以及Product类型的oldValue,该方法会抛出异常Exception,其返回值是通过使用fetchFromDB方法并传入productId来从数据库获取数据后返回 。
私有产品,从数据库获取,通过长整形产品标识,如果该产品标识不在正在加载的锁集合中,就计算得到获取锁并将其设置为原子布尔值,然后进入循环。在循环中,每次尝试将原子布尔值从假设置为真,如果设置失败,就会让出当前线程,也就是反复尝试设置原子布尔值,直到成功为止,最终完成从数据库获取产品的操作。
尝试 { 获取缓存中的产品,这个产品通过产品ID来查询,如果获取到的结果不为空,那么返回这个获取到的结果。 } 若获取到的上述查询结果为空,那么就返回 null 。 若获取到缓存中的这个产品,这个缓存中的产品通过产品ID查询得到,且非空,那么就返回这个缓存中的产品(同一个)。 说明:这里按照要求进行。
System.out.println("从数据库加载: " + 产品ID); 线程.sleep(100); 返回新的产品(产品ID, "产品-" + 产品ID, 线程本地随机.current()..nextInt(100));。
} 当捕获到异常 e 时(这里的异常 e 是指 Exception 类型 ),抛出一个新的运行时异常 RuntimeException ,该异常的消息内容是 "DB error" ,并且将异常 e 作为参数传递进去 。 } 最后执行 ,将锁 lock 设置为 false 。 从加载锁集合 loadingLocks 中将产品 ID 为 productId 的锁移除 。 } } }。
_public_ _static_ _class_ _Product_ { _private_ _final_ _Long_ _id_; _private_ _final_ _String_ _name_; _private_ _final_ _int_ _stock_; } ,将其改写为:有一个公共的静态类名为产品,其中有一个私有的最终的长整型的id,还有一个私有的最终的字符串类型的名称,另外还有一个私有的最终的整型的库存 。
公开一个具有 Long 类型的 id、String 类型的名称以及 int 类型的库存的产品,这个产品里的 id 是那个传入到这里的 Long 类型的 id,名称是那个传入到这里的 String 类型的名称,库存是那个传入到这里的 int 类型的库存。 } } 。
公共静态的产品获取方法,通过一个长整型的产品标识来获取产品,返回的是缓存中根据那个长整型产品标识所获取到的产品 。
公共的静态的无效的主方法,在此方法里存在字符串数组类型的参数,并且会抛出中断被线程的异常,在该异常处理的代码块中,执行器服务的实例被创建,此实例是通过使用能创建固定大小线程池的方法并传入数字20来得到的,之后,在一个循环结构里,有一个整型变量从0开始,每次循环增加1 。< 100; i++) { long productId = i % 10; pool.execute(() ->抓取具体的数据,从产品标识那里获取,得到一个产品实例,把它赋值给变量p,然后输出一行文字,文字内容是获取到的产品实例的名称,之后关闭线程池pool,最后它使用时间单元秒去等待线程池子一整秒,看是否能完成所有的任务 。
系统输出打印行,内容为“缓存命中率: ”加上缓存统计信息中的命中比率,随后结束,结束之后再结束 。
设计思路方面有防击穿措施,采用的是运用同步锁以及其中带有的CAS操作,以此来确保单个Key仅仅加载一次,还有防雪崩措施,是通过异步刷新以及随机过期时间来达成,另外存在热点保护,是基于相关情况以及自动管理热点数据来实现 。
在实际大型项目里,鉴于本地内存存在一定局限性这一状况,在大多数场景之下,会运用与Redis相关的多级缓存架构来处理 。
总结
彼时我们于使用之际,依据内存压力予以调整,以此来避免频繁淘汰。借由创新的算法以及精细化并发控制二者,于缓存命中率跟吞吐量之间达成平衡。本文之中的实战案例展示了怎样凭借异步加载、自动刷新等机制,构建出稳定高效的缓存层来。
适用的场景是,存在某种业务flinto for sketch,这种业务具备读多写少的特点,以及高并发的情况,还存在低延迟的需求,比如类似电商、社交平台那些业务 。
如有侵权请联系删除!
Copyright © 2023 江苏优软数字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服务提供商
13262879759
微信二维码