欢迎关注公众号【拿客】,引导用户星标获取最新技术内容,内容聚焦低延迟架构、AI智能体、高并发系统设计领域。
引言
在现代软件系统架构中,缓存作为提升性能、降低延迟、减轻后端压力的关键组件,已经成为高并发系统不可或缺的一部分。随着互联网业务的快速发展,用户量和数据量呈现爆炸式增长,系统面临的性能挑战也越来越大。根据亚马逊AWS的统计数据,在分布式系统中,缓存层的引入可以将系统整体性能提升10-100倍,同时降低后端存储成本30%-70%。从最基础的HashMap到复杂的分布式缓存系统,缓存技术经历了长期的发展历程,每一代缓存技术的演进都为系统架构优化提供了新的可能。
而在Java生态系统中,Caffeine作为一款高性能的内存缓存库,凭借其卓越的性能、丰富的功能和灵活的配置,正日益受到开发者的青睐。根据近年来的技术趋势观察,Caffeine在Java本地缓存领域的采用率显著上升,已逐渐成为Guava Cache的主要替代方案。Caffeine不仅被Apache、Spring等开源基金会的顶级项目采用,也被阿里巴巴、字节跳动、Netflix、LinkedIn等国内外顶级互联网公司广泛应用于核心业务系统。
本文将全面深入地解析Caffeine缓存技术,从其设计哲学、底层架构、核心功能,到性能对比、最佳实践和常见问题,为读者呈现一个完整的Caffeine技术图谱。本文不仅会深入剖析Caffeine的底层实现原理,还会结合大量生产环境的实际案例,帮助读者理解如何在不同业务场景下正确使用Caffeine,避免常见的缓存陷阱。无论你是刚开始接触缓存技术的新手,还是有着丰富经验的架构师,都能从本文中获得有价值的信息。通过本文的学习,你将能够:
- 深入理解Caffeine的设计理念和底层实现原理
- 掌握Caffeine的各种高级功能和配置方法
- 学会在不同业务场景下选择合适的缓存策略
- 避免缓存使用中的常见问题和陷阱
- 构建高性能、高可靠的缓存架构
- 提升系统的整体性能和稳定性
本文的内容安排如下:第一章介绍Caffeine的基本概念和发展历程;第二章深入分析Caffeine的核心设计理念;第三章详细解析Caffeine的底层架构和实现原理;第四章全面介绍Caffeine的核心功能;第五章对Caffeine的性能进行详细分析并与其他缓存方案进行对比;第六章总结Caffeine的最佳实践;第七章分析Caffeine使用中的常见问题和解决方案;第八章介绍Caffeine的高级应用场景;第九章介绍Caffeine的生态系统和集成案例;第十章对全文进行总结并展望缓存技术的未来发展方向。
第一章 Caffeine概述
1.1 Caffeine是什么
Caffeine是一款基于Java 8开发的高性能、近最优的内存缓存库,由Ben Manes主导开发,其设计灵感来源于Google Guava Cache和ConcurrentLinkedHashMap项目。Caffeine提供了与Guava Cache相似的API,但在性能上有了显著提升,同时增加了许多高级功能,如异步加载、多种过期策略、统计监控等。
作为一款内存缓存,Caffeine主要解决的是应用层的性能瓶颈问题,通过将频繁访问的数据存储在内存中,避免了重复计算或频繁访问数据库等慢速存储系统,从而大幅提升系统的响应速度和吞吐量。
1.2 Caffeine的发展历程
Caffeine的发展可以追溯到2014年,最初是作为Guava Cache的改进版本而诞生。Guava Cache虽然功能强大,但在高并发场景下存在一些性能瓶颈,特别是在 eviction 策略的实现上。Caffeine的作者Ben Manes在ConcurrentLinkedHashMap项目的基础上,结合了最新的缓存算法研究成果,开发出了Caffeine。
经过多年的发展,Caffeine已经成为Java生态中最受欢迎的缓存库之一,被广泛应用于各大开源项目和企业系统中,包括Apache Cassandra、Apache Kafka、Spring Framework等知名项目都集成了Caffeine作为其内部缓存组件。
1.3 Caffeine的核心优势
Caffeine之所以能够在众多缓存方案中脱颖而出,主要得益于以下几个核心优势:
极致的性能:Caffeine采用了现代化的并发设计和优化的 eviction 算法,在高并发场景下的性能远超Guava Cache、Ehcache等传统缓存方案,接近理论最优值。
丰富的功能特性:支持大小基于、时间基于、引用基于等多种eviction策略,支持异步加载、自动刷新、移除监听、统计监控等高级功能,能够满足各种复杂的业务场景需求。
极低的内存开销:通过精心设计的内存布局和数据结构,Caffeine在相同缓存容量下的内存开销比其他缓存库低得多,能够更高效地利用内存资源。
良好的API设计:提供了流畅的Builder API和与Guava Cache相似的接口,学习成本低,迁移方便,同时支持JSR-107 JCache标准和Guava适配器。
活跃的社区支持:项目维护活跃,更新频繁,问题响应快,有着丰富的文档和社区资源。
1.4 Caffeine的应用场景
Caffeine作为一款通用的内存缓存库,适用于几乎所有需要缓存的Java应用场景,在不同行业和业务领域都有着广泛的应用:
1.4.1 互联网电商领域
在电商系统中,Caffeine被广泛应用于各个层级:
- 商品详情页缓存:缓存热点商品的详情信息,包括价格、库存、描述等,这些信息的访问量极大,使用Caffeine缓存后可以将响应时间从几十毫秒降低到几毫秒,同时减少数据库压力90%以上。
- 购物车缓存:缓存用户的购物车数据,用户每次添加、删除商品都需要访问购物车,使用本地缓存可以避免频繁调用分布式缓存或数据库。
- 推荐结果缓存:缓存AI推荐系统生成的用户个性化推荐结果,推荐计算通常耗时较长,缓存后可以大幅提升用户体验。
- 促销活动缓存:秒杀、大促等活动期间,缓存活动规则、商品库存、优惠券信息等热点数据,支撑突发的高并发访问。
根据阿里巴巴的实践经验,在电商的商品详情页系统中引入Caffeine作为一级缓存后,整个系统的吞吐量提升了300%,数据库的QPS从原来的10万降低到不足1万,大大降低了系统的运行成本。
1.4.2 金融支付领域
在金融支付系统中,Caffeine的高性能和高可靠性显得尤为重要:
- 用户信息缓存:缓存用户的账户信息、权限信息、风险控制规则等,这些信息在每次支付请求中都需要访问,低延迟的缓存可以大幅提升支付处理速度。
- 路由规则缓存:缓存支付路由的规则配置,根据用户的银行卡、金额、地区等信息动态选择最优的支付通道,缓存这些规则可以避免每次都查询配置中心。
- 风控策略缓存:缓存风险控制的策略模型和黑白名单数据,风控判断需要在极短时间内完成,本地缓存可以保证风控判断的低延迟。
- 交易流水缓存:缓存最近的交易流水数据,用于快速的重复性校验和幂等性判断,避免重复支付等问题。
在某大型支付系统的实践中,使用Caffeine缓存用户信息和风控规则后,单笔支付的平均处理时间从50毫秒降低到15毫秒,系统每秒能够处理的交易笔数从2万提升到8万,完全满足了业务高峰期的需求。
1.4.3 社交网络领域
在社交网络应用中,Caffeine能够很好地应对海量用户的访问压力:
- 用户资料缓存:缓存用户的基本资料、头像、关系链等信息,这些信息在用户浏览动态、发送消息、查看他人主页时都会被频繁访问。
- 内容缓存:缓存热点的帖子、视频、图片的元数据,这些内容的访问量呈现明显的幂律分布,少数热点内容占据了大部分的访问流量。
- 消息队列缓存:缓存用户的消息列表和通知信息,用户每次打开APP都会拉取最新的消息,使用本地缓存可以减少对存储系统的访问压力。
- 计数缓存:缓存点赞数、评论数、转发数等计数信息,这些信息的更新频率极高,使用缓存可以避免频繁的数据库写操作。
某短视频平台在使用Caffeine缓存热点视频的元数据后,内容访问的平均延迟从200毫秒降低到25毫秒,系统的整体吞吐量提升了400%,同时存储系统的成本降低了60%。
1.4.4 其他通用场景
除了上述行业特定场景外,Caffeine还适用于各种通用的技术场景:
- Web应用层缓存:缓存用户会话、页面片段、API响应等,提升Web应用的响应速度,特别适合前后端分离架构中的接口层缓存。
- 数据库查询缓存:缓存频繁访问的数据库查询结果,尤其是复杂的多表关联查询和聚合查询,减少数据库压力,避免重复计算。
- 计算结果缓存:缓存昂贵的计算结果,如AI模型推理、复杂业务计算、数据报表生成等,这些计算通常需要几百毫秒甚至几秒的时间,缓存后可以大幅提升用户体验。
- 第三方API调用缓存:缓存第三方服务的调用结果,减少网络开销和调用次数,同时避免第三方服务的限流和费用支出,特别适合调用量较大的地图服务、短信服务、天气预报服务等。
- 元数据缓存:缓存系统配置、字典数据、用户权限等不经常变动的元数据,这些数据的访问频率极高但更新频率很低,非常适合使用本地缓存。
- 微服务架构中的本地缓存:作为分布式缓存的一级缓存,减少跨网络调用,降低分布式缓存的压力,同时提升系统的整体可用性,即使分布式缓存出现故障,本地缓存仍然可以支撑系统的基本运行。
第二章 Caffeine核心设计理念
2.1 设计目标
Caffeine的设计目标非常明确:在保证功能丰富性和API易用性的前提下,追求极致的性能和内存效率。这一目标的提出是基于对当时缓存技术现状的深刻反思。在Caffeine诞生之前,Java生态中的缓存库要么性能不足,要么功能缺失,要么内存开销过大,无法满足日益增长的高并发系统需求。Caffeine的作者Ben Manes在多年缓存研究的基础上,结合工业界的实际需求,提出了Caffeine的五大设计原则:
2.1.1 性能优先原则
所有设计决策都首先考虑性能影响,通过算法优化和并发设计最大化吞吐量、最小化延迟。为了实现这一目标,Caffeine的开发团队做了大量的性能测试和优化工作,甚至为了几纳秒的性能提升而反复调整数据结构和代码实现。在Caffeine的开发过程中,性能是最重要的考量因素,任何可能影响性能的功能都会被谨慎评估,只有在性能影响可以接受的情况下才会被加入。
这种极致的性能追求使得Caffeine在各种基准测试中都远远领先于其他缓存库,即使在最高的并发压力下,Caffeine仍然能够保持稳定的低延迟和高吞吐量。这对于现代高并发系统来说至关重要,因为缓存层通常是系统的性能瓶颈所在,缓存性能的一点点提升都可能带来整个系统性能的巨大提升。
2.1.2 内存高效原则
精心设计数据结构和内存布局,最小化每个缓存条目的内存开销。在Java应用中,内存是宝贵的资源,尤其是在大规模部署的微服务架构中,每个服务实例的内存大小都受到限制。如果缓存的内存开销过大,就会占用过多的堆内存,导致GC频繁,反而影响系统的整体性能。
Caffeine的开发团队在内存优化上做了大量工作,从对象头的大小到字段的排列顺序,都进行了精心的设计。例如,Caffeine使用字节和短整型来存储元数据,而不是使用整型和长整型;使用单个对象来存储多个相关的元数据,减少对象头的开销;甚至利用Java的unsafe API直接操作内存,进一步减少内存开销。这些优化使得Caffeine在相同缓存容量下的内存开销比Guava Cache低约30%,比Ehcache低约50%。
2.1.3 API友好原则
提供直观、流畅的API,降低学习成本,同时保持与现有标准的兼容性。Caffeine的API设计借鉴了Guava Cache的优秀设计,大部分Guava Cache的用户几乎不需要修改代码就可以迁移到Caffeine。同时,Caffeine还提供了更丰富的API来支持其高级功能,这些API的设计都遵循了流畅的Builder模式,使用起来非常直观。
此外,Caffeine还支持JSR-107 JCache标准,用户可以通过标准的JCache API来使用Caffeine,这样可以方便地在不同缓存实现之间切换。API友好性的设计使得Caffeine的学习成本非常低,开发者可以快速上手,不需要花费大量时间学习新的API。
2.1.4 功能完备原则
支持各种常见和高级的缓存功能,满足不同场景的需求。Caffeine不仅提供了基本的缓存增删改查功能,还提供了异步加载、自动刷新、多种过期策略、移除监听、统计监控、权重计算、引用支持等高级功能。这些功能都是在广泛调研工业界实际需求的基础上开发的,能够满足几乎所有业务场景的需求。
在功能开发过程中,Caffeine的开发团队非常注重功能的实用性,不会为了炫技而添加无用的功能。每个新功能的加入都会经过严格的评审,确保其确实能够解决实际的业务问题,同时不会对性能产生过大的影响。这种实用主义的设计理念使得Caffeine的功能集非常精炼,每个功能都有其明确的应用场景。
2.1.5 可观测性原则
提供丰富的统计和监控接口,方便用户了解缓存的运行状态和性能表现。在生产环境中,可观测性是系统可靠性的重要保障。Caffeine提供了详细的统计信息,包括命中率、命中次数、未命中次数、加载成功次数、加载失败次数、总加载时间、驱逐次数等关键指标。这些指标可以方便地对接Prometheus、Grafana等监控系统,实现对缓存的可视化监控和报警。
可观测性的设计使得用户可以实时了解缓存的运行状态,及时发现和解决缓存相关的问题。例如,如果命中率突然下降,可能是缓存大小不足或者过期时间设置不合理;如果加载失败率升高,可能是后端存储出现了问题。这些指标为系统的维护和优化提供了重要的依据。
2.2 并发模型设计
Caffeine的并发模型是其高性能的核心保障。与传统的使用分段锁的缓存实现不同,Caffeine采用了更加现代化的并发设计,实现了无锁读取和异步写入的架构:
内部数据结构
后台处理线程
事件队列
哈希表
Caffeine缓存
用户线程
内部数据结构
后台处理线程
事件队列
哈希表
Caffeine缓存
用户线程
#mermaid-svg-y0LLke1JVgnYsAi4{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y0LLke1JVgnYsAi4 .error-icon{fill:#552222;}#mermaid-svg-y0LLke1JVgnYsAi4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y0LLke1JVgnYsAi4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y0LLke1JVgnYsAi4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y0LLke1JVgnYsAi4 .marker.cross{stroke:#333333;}#mermaid-svg-y0LLke1JVgnYsAi4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y0LLke1JVgnYsAi4 p{margin:0;}#mermaid-svg-y0LLke1JVgnYsAi4 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y0LLke1JVgnYsAi4 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-y0LLke1JVgnYsAi4 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-y0LLke1JVgnYsAi4 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-y0LLke1JVgnYsAi4 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-y0LLke1JVgnYsAi4 .sequenceNumber{fill:white;}#mermaid-svg-y0LLke1JVgnYsAi4 #sequencenumber{fill:#333;}#mermaid-svg-y0LLke1JVgnYsAi4 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-y0LLke1JVgnYsAi4 .messageText{fill:#333;stroke:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y0LLke1JVgnYsAi4 .labelText,#mermaid-svg-y0LLke1JVgnYsAi4 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .loopText,#mermaid-svg-y0LLke1JVgnYsAi4 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-y0LLke1JVgnYsAi4 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-y0LLke1JVgnYsAi4 .noteText,#mermaid-svg-y0LLke1JVgnYsAi4 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-y0LLke1JVgnYsAi4 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y0LLke1JVgnYsAi4 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y0LLke1JVgnYsAi4 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y0LLke1JVgnYsAi4 .actorPopupMenu{position:absolute;}#mermaid-svg-y0LLke1JVgnYsAi4 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-y0LLke1JVgnYsAi4 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y0LLke1JVgnYsAi4 .actor-man circle,#mermaid-svg-y0LLke1JVgnYsAi4 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-y0LLke1JVgnYsAi4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
读取过程完全无锁,仅更新访问时间
访问时间更新异步提交到事件队列
写操作立即返回,结构变更异步处理
loop
[异步处理]
get(key)
查找key
返回条目
返回value
提交访问事件
put(key, value)
更新条目
立即返回
提交写入事件
批量获取事件
返回事件列表
更新LRU/LFU队列、时间轮等
处理过期、驱逐等逻辑
2.2.1 无锁读取
Caffeine的读取操作完全是无锁的,通过使用Java的原子类和volatile关键字保证内存可见性,使得在高并发读场景下不会有锁竞争的开销。读取过程仅更新条目的元数据(如访问时间),这些更新操作会被异步提交到事件队列,不会阻塞读取线程。这对于读多写少的缓存场景来说,性能提升非常显著,读取吞吐量可以达到每秒数千万次。
2.2.2 异步写入
所有对缓存结构有修改的操作(如写入、更新、删除)都会被包装成事件,放入一个异步队列中,由专门的线程后台处理。这种设计使得写入操作不会阻塞读取操作,也不会因为多个写入线程之间的锁竞争而导致性能下降。事件队列采用了无锁队列实现,多个线程可以同时提交事件而不会产生锁竞争。
2.2.3 事件驱动架构
Caffeine内部采用了事件驱动的架构,所有状态变更都通过事件来传递和处理。后台线程会批量处理事件队列中的事件,批量更新内部数据结构(如LRU队列、LFU频率草图、时间轮等)。这种批量处理的设计进一步减少了锁竞争,提高了整体吞吐量。事件驱动架构不仅提高了并发性能,还使得代码结构更加清晰,易于扩展和维护。
2.3 内存模型设计
Caffeine的内存模型经过精心设计,在保证功能的前提下,尽可能减少内存开销:
2.3.1 紧凑的数据结构
Caffeine使用了一系列紧凑的数据结构来存储缓存条目,避免不必要的对象包装和内存对齐开销。每个缓存条目只存储必要的元数据,如键、值、访问时间、写入时间等,没有多余的字段。
2.3.2 引用类型优化
支持将键和值存储为弱引用或软引用,使得缓存能够自动响应JVM的内存压力,在内存不足时自动释放不常访问的缓存条目,避免OOM问题。
2.3.3 内存占用估算
Caffeine能够根据配置自动估算缓存的内存占用,用户可以通过设置最大内存大小而不是最大条目数来限制缓存的内存使用,这对于存储大小不一的缓存条目非常有用。
第三章 Caffeine底层架构与实现原理
3.1 整体架构
Caffeine的整体架构采用分层设计,各个模块职责明确,耦合度低,便于扩展和维护。整体架构可以分为几个核心层次:
#mermaid-svg-tK3esUYzFCu2NnFs{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tK3esUYzFCu2NnFs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tK3esUYzFCu2NnFs .error-icon{fill:#552222;}#mermaid-svg-tK3esUYzFCu2NnFs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tK3esUYzFCu2NnFs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tK3esUYzFCu2NnFs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tK3esUYzFCu2NnFs .marker.cross{stroke:#333333;}#mermaid-svg-tK3esUYzFCu2NnFs svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tK3esUYzFCu2NnFs p{margin:0;}#mermaid-svg-tK3esUYzFCu2NnFs .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tK3esUYzFCu2NnFs .cluster-label text{fill:#333;}#mermaid-svg-tK3esUYzFCu2NnFs .cluster-label span{color:#333;}#mermaid-svg-tK3esUYzFCu2NnFs .cluster-label span p{background-color:transparent;}#mermaid-svg-tK3esUYzFCu2NnFs .label text,#mermaid-svg-tK3esUYzFCu2NnFs span{fill:#333;color:#333;}#mermaid-svg-tK3esUYzFCu2NnFs .node rect,#mermaid-svg-tK3esUYzFCu2NnFs .node circle,#mermaid-svg-tK3esUYzFCu2NnFs .node ellipse,#mermaid-svg-tK3esUYzFCu2NnFs .node polygon,#mermaid-svg-tK3esUYzFCu2NnFs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tK3esUYzFCu2NnFs .rough-node .label text,#mermaid-svg-tK3esUYzFCu2NnFs .node .label text,#mermaid-svg-tK3esUYzFCu2NnFs .image-shape .label,#mermaid-svg-tK3esUYzFCu2NnFs .icon-shape .label{text-anchor:middle;}#mermaid-svg-tK3esUYzFCu2NnFs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tK3esUYzFCu2NnFs .rough-node .label,#mermaid-svg-tK3esUYzFCu2NnFs .node .label,#mermaid-svg-tK3esUYzFCu2NnFs .image-shape .label,#mermaid-svg-tK3esUYzFCu2NnFs .icon-shape .label{text-align:center;}#mermaid-svg-tK3esUYzFCu2NnFs .node.clickable{cursor:pointer;}#mermaid-svg-tK3esUYzFCu2NnFs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tK3esUYzFCu2NnFs .arrowheadPath{fill:#333333;}#mermaid-svg-tK3esUYzFCu2NnFs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tK3esUYzFCu2NnFs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tK3esUYzFCu2NnFs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tK3esUYzFCu2NnFs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tK3esUYzFCu2NnFs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tK3esUYzFCu2NnFs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tK3esUYzFCu2NnFs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tK3esUYzFCu2NnFs .cluster text{fill:#333;}#mermaid-svg-tK3esUYzFCu2NnFs .cluster span{color:#333;}#mermaid-svg-tK3esUYzFCu2NnFs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tK3esUYzFCu2NnFs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tK3esUYzFCu2NnFs rect.text{fill:none;stroke-width:0;}#mermaid-svg-tK3esUYzFCu2NnFs .icon-shape,#mermaid-svg-tK3esUYzFCu2NnFs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tK3esUYzFCu2NnFs .icon-shape p,#mermaid-svg-tK3esUYzFCu2NnFs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tK3esUYzFCu2NnFs .icon-shape rect,#mermaid-svg-tK3esUYzFCu2NnFs .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tK3esUYzFCu2NnFs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tK3esUYzFCu2NnFs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tK3esUYzFCu2NnFs :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
并发控制层
无锁读取
异步写入
事件驱动
线程协调
数据存储层
哈希表
频率草图
时间轮
访问队列
写入队列
核心功能层
加载逻辑
驱逐策略
过期管理
自动刷新
统计监控
事件通知
配置层
参数解析
策略配置
校验逻辑
API层
Cache
LoadingCache
AsyncCache
AsyncLoadingCache
API层
配置层
核心功能层
数据存储层
并发控制层
3.2 核心数据结构
Caffeine内部使用了多种高效的数据结构来实现其功能:
3.2.1 哈希表
Caffeine使用自定义的开放式寻址哈希表来存储缓存条目,相比Java自带的HashMap,这种哈希表在内存使用和并发访问上都做了优化,能够更高效地处理缓存场景下的访问模式。
3.2.2 频率草图(Frequency Sketch)
为了实现高效的LFU(Least Frequently Used) eviction策略,Caffeine使用了一种叫做频率草图的数据结构来记录每个键的访问频率。这种数据结构借鉴了布隆过滤器的设计思想,能够在很小的内存开销下,近似地记录每个条目的访问频率,同时避免了传统LFU算法需要大量内存存储频率计数的问题。
频率草图的实现原理非常巧妙:它使用4个独立的哈希函数和一个字节数组来存储频率信息,每个字节被分成两个4位的计数器,因此每个字节可以存储两个条目的频率信息。每个4位计数器能够记录0-15的频率值,当计数器达到最大值15时,就不会再递增。当有新的访问时,会对4个哈希函数对应的位置进行递增操作,最终取4个计数器中的最小值作为该条目的频率估计。这种设计利用了哈希函数的独立性,减少了哈希冲突带来的误差,在保证准确性的同时,极大地减少了内存开销。
为了适应访问模式的变化,频率草图还实现了老化机制:当整个草图的总计数达到一定阈值时,会对所有计数器进行右移一位的操作,相当于将所有频率计数减半。这种老化机制使得新的访问模式能够逐渐覆盖旧的访问模式,避免了\”历史包袱\”问题,使得算法能够适应访问模式的变化。
频率草图的内存开销相对可控,对于一个容量为100万条目的缓存,频率草图需要大约2MB的内存(计算公式:4个哈希函数 × 100万条目 ÷ 2个计数器每字节)。虽然仍比传统LFU算法需要为每个条目存储完整计数器的内存开销要小得多,但比一些早期文献中提到的数值要大。频率草图的查询和更新操作都是O(1)的时间复杂度,非常高效。根据公开资料,频率草图的频率估计准确率在实践中表现良好,能够满足缓存eviction的需求。
3.2.3 时间轮(Timer Wheel)
对于时间过期的处理,Caffeine使用了时间轮算法来高效管理过期条目。时间轮是一种环形的数据结构,每个槽位代表一个时间片,条目根据其过期时间被放入对应的槽位中。随着时间的推移,时间轮不断转动,处理到期的条目。
#mermaid-svg-eg1SkajJsgIEIBVi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eg1SkajJsgIEIBVi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eg1SkajJsgIEIBVi .error-icon{fill:#552222;}#mermaid-svg-eg1SkajJsgIEIBVi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eg1SkajJsgIEIBVi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eg1SkajJsgIEIBVi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eg1SkajJsgIEIBVi .marker.cross{stroke:#333333;}#mermaid-svg-eg1SkajJsgIEIBVi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eg1SkajJsgIEIBVi p{margin:0;}#mermaid-svg-eg1SkajJsgIEIBVi .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-eg1SkajJsgIEIBVi .cluster-label text{fill:#333;}#mermaid-svg-eg1SkajJsgIEIBVi .cluster-label span{color:#333;}#mermaid-svg-eg1SkajJsgIEIBVi .cluster-label span p{background-color:transparent;}#mermaid-svg-eg1SkajJsgIEIBVi .label text,#mermaid-svg-eg1SkajJsgIEIBVi span{fill:#333;color:#333;}#mermaid-svg-eg1SkajJsgIEIBVi .node rect,#mermaid-svg-eg1SkajJsgIEIBVi .node circle,#mermaid-svg-eg1SkajJsgIEIBVi .node ellipse,#mermaid-svg-eg1SkajJsgIEIBVi .node polygon,#mermaid-svg-eg1SkajJsgIEIBVi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eg1SkajJsgIEIBVi .rough-node .label text,#mermaid-svg-eg1SkajJsgIEIBVi .node .label text,#mermaid-svg-eg1SkajJsgIEIBVi .image-shape .label,#mermaid-svg-eg1SkajJsgIEIBVi .icon-shape .label{text-anchor:middle;}#mermaid-svg-eg1SkajJsgIEIBVi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eg1SkajJsgIEIBVi .rough-node .label,#mermaid-svg-eg1SkajJsgIEIBVi .node .label,#mermaid-svg-eg1SkajJsgIEIBVi .image-shape .label,#mermaid-svg-eg1SkajJsgIEIBVi .icon-shape .label{text-align:center;}#mermaid-svg-eg1SkajJsgIEIBVi .node.clickable{cursor:pointer;}#mermaid-svg-eg1SkajJsgIEIBVi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eg1SkajJsgIEIBVi .arrowheadPath{fill:#333333;}#mermaid-svg-eg1SkajJsgIEIBVi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eg1SkajJsgIEIBVi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eg1SkajJsgIEIBVi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eg1SkajJsgIEIBVi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eg1SkajJsgIEIBVi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eg1SkajJsgIEIBVi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eg1SkajJsgIEIBVi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eg1SkajJsgIEIBVi .cluster text{fill:#333;}#mermaid-svg-eg1SkajJsgIEIBVi .cluster span{color:#333;}#mermaid-svg-eg1SkajJsgIEIBVi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eg1SkajJsgIEIBVi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eg1SkajJsgIEIBVi rect.text{fill:none;stroke-width:0;}#mermaid-svg-eg1SkajJsgIEIBVi .icon-shape,#mermaid-svg-eg1SkajJsgIEIBVi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eg1SkajJsgIEIBVi .icon-shape p,#mermaid-svg-eg1SkajJsgIEIBVi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eg1SkajJsgIEIBVi .icon-shape rect,#mermaid-svg-eg1SkajJsgIEIBVi .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eg1SkajJsgIEIBVi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eg1SkajJsgIEIBVi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eg1SkajJsgIEIBVi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
时间轮结构
槽位0当前时间
槽位11时间片后
槽位22时间片后
槽位33时间片后
槽位44时间片后
槽位55时间片后
槽位66时间片后
槽位77时间片后
新条目 过期时间=3
新条目 过期时间=5
新条目 过期时间=7
时间指针
每过一个时间片指针向前移动
指针到达的槽位处理所有过期条目
Caffeine的时间轮实现采用了分层设计,支持不同粒度的时间过期,从毫秒级到小时级都能高效处理。相比传统的使用优先级队列管理过期条目的方式,时间轮算法的插入和删除操作的时间复杂度都是O(1),非常适合处理大量过期条目的场景。对于有百万级甚至千万级条目的缓存,时间轮算法的性能优势尤为明显。
3.2.4 访问队列和写入队列
为了实现LRU(Least Recently Used)和LFU的混合eviction策略,Caffeine维护了几个不同的队列:
- 访问队列:记录条目的访问顺序,用于实现LRU策略。
- 写入队列:记录条目的写入顺序,用于处理基于写入时间的过期。
- 缓刑队列:新加入的条目首先进入缓刑队列,经过一段时间的观察后,访问频率高的条目才会进入主缓存区域。
3.3 eviction算法实现
Caffeine的eviction算法是其最核心的创新之一,它结合了LRU和LFU的优点,采用了一种叫做TinyLFU的新型eviction策略,能够在各种访问模式下都达到接近最优的命中率。
3.3.1 传统eviction算法的问题
传统的缓存eviction算法各有优缺点:
- FIFO(先进先出):实现简单,但命中率低,没有考虑访问频率和 recency。
- LRU(最近最少使用):优先淘汰最久未访问的条目,对于有时间局部性的访问模式效果好,但对于突发流量会污染缓存。
- LFU(最不经常使用):优先淘汰访问频率最低的条目,对于有频率局部性的访问模式效果好,但需要额外空间存储频率信息,且不适应访问模式的变化。
3.3.2 TinyLFU算法原理
TinyLFU是一种高效的近似LFU算法,它解决了传统LFU的几个问题:
3.3.3 W-TinyLFU优化
Caffeine在TinyLFU的基础上做了进一步优化,提出了W-TinyLFU(Window TinyLFU)算法。这种算法将缓存分为两个区域,结合了LRU和LFU的优点:
#mermaid-svg-ty5A8vSAgXIPI7Q6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .error-icon{fill:#552222;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .marker.cross{stroke:#333333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 p{margin:0;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster-label text{fill:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster-label span{color:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster-label span p{background-color:transparent;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .label text,#mermaid-svg-ty5A8vSAgXIPI7Q6 span{fill:#333;color:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .node rect,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node circle,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node ellipse,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node polygon,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .rough-node .label text,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node .label text,#mermaid-svg-ty5A8vSAgXIPI7Q6 .image-shape .label,#mermaid-svg-ty5A8vSAgXIPI7Q6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .rough-node .label,#mermaid-svg-ty5A8vSAgXIPI7Q6 .node .label,#mermaid-svg-ty5A8vSAgXIPI7Q6 .image-shape .label,#mermaid-svg-ty5A8vSAgXIPI7Q6 .icon-shape .label{text-align:center;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .node.clickable{cursor:pointer;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .arrowheadPath{fill:#333333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ty5A8vSAgXIPI7Q6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ty5A8vSAgXIPI7Q6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster text{fill:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .cluster span{color:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ty5A8vSAgXIPI7Q6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .icon-shape,#mermaid-svg-ty5A8vSAgXIPI7Q6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .icon-shape p,#mermaid-svg-ty5A8vSAgXIPI7Q6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .icon-shape rect,#mermaid-svg-ty5A8vSAgXIPI7Q6 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ty5A8vSAgXIPI7Q6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ty5A8vSAgXIPI7Q6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ty5A8vSAgXIPI7Q6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
主区域
Probation 队列 20%
Protected 队列 80%
新条目
窗口区域 Window LRU 1%
窗口满?
晋升到 probation 队列
保留在窗口区域
主区域 Main 99%
访问 probation 条目
频率足够高?
晋升到 protected 队列
留在 probation 队列
需要驱逐时
比较 probation 队列中条目频率
淘汰频率最低的条目
protected 队列满时
将LRU条目降级到 probation 队列
- 窗口区域(Window):占用总缓存大小的1%,采用LRU策略,用于处理突发的短期访问流量,避免偶发的访问模式导致热点数据被淘汰。
- 主区域(Main):占用总缓存大小的99%,采用TinyLFU策略,用于存储长期频繁访问的条目。主区域又分为两部分:
- Probation(缓刑)区:占主区域的20%,存储新晋升的条目,需要进一步观察其访问频率。
- Protected(受保护)区:占主区域的80%,存储访问频率较高的条目,被淘汰的优先级较低。
当新条目进入缓存时,首先进入窗口区域。当窗口区域满时,会将LRU条目\”晋升\”到主区域的 probation 队列。在主区域中,当probation区的条目被访问且频率足够高时,会晋升到protected区;当protected区满时,会将最久未访问的条目降级到probation区。当需要evict时,会比较probation队列中的条目的频率,淘汰频率较低的条目。
这种混合策略结合了LRU对短期流量的适应性和LFU对长期热点数据的识别能力,在各种访问模式下都能获得较好的命中率。根据公开的研究资料和社区反馈,W-TinyLFU相比传统LRU算法命中率通常能提升5-15%,具体提升幅度取决于访问模式的特征。
3.4 过期策略实现
Caffeine支持三种时间过期策略,每种策略的实现方式都经过了优化,能够高效处理百万级别的过期条目。缓存条目的完整生命周期如下:
#mermaid-svg-rxYo4GPQWHWZKQtw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rxYo4GPQWHWZKQtw .error-icon{fill:#552222;}#mermaid-svg-rxYo4GPQWHWZKQtw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rxYo4GPQWHWZKQtw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rxYo4GPQWHWZKQtw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw .marker.cross{stroke:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rxYo4GPQWHWZKQtw p{margin:0;}#mermaid-svg-rxYo4GPQWHWZKQtw defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-rxYo4GPQWHWZKQtw g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-rxYo4GPQWHWZKQtw g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-rxYo4GPQWHWZKQtw g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-rxYo4GPQWHWZKQtw g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-rxYo4GPQWHWZKQtw .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-rxYo4GPQWHWZKQtw .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rxYo4GPQWHWZKQtw .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-rxYo4GPQWHWZKQtw .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-rxYo4GPQWHWZKQtw .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-rxYo4GPQWHWZKQtw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rxYo4GPQWHWZKQtw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rxYo4GPQWHWZKQtw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rxYo4GPQWHWZKQtw .edgeLabel .label text{fill:#333;}#mermaid-svg-rxYo4GPQWHWZKQtw .label div .edgeLabel{color:#333;}#mermaid-svg-rxYo4GPQWHWZKQtw .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-rxYo4GPQWHWZKQtw .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-rxYo4GPQWHWZKQtw .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-rxYo4GPQWHWZKQtw .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw #statediagram-barbEnd{fill:#333333;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rxYo4GPQWHWZKQtw .cluster-label,#mermaid-svg-rxYo4GPQWHWZKQtw .nodeLabel{color:#131300;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-rxYo4GPQWHWZKQtw .note-edge{stroke-dasharray:5;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-note text{fill:black;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram-note .nodeLabel{color:black;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagram .edgeLabel{color:red;}#mermaid-svg-rxYo4GPQWHWZKQtw #dependencyStart,#mermaid-svg-rxYo4GPQWHWZKQtw #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-rxYo4GPQWHWZKQtw .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rxYo4GPQWHWZKQtw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
put/load
访问/更新
访问刷新
超过访问/写入时间
时间轮到期处理
缓存满且频率低
后台线程处理
显式调用remove
弱/软引用被GC回收





