Carrot Cache:高性能、SSD友好的Java缓存库


Carrot Cache(CC)项目旨在实现数据缓存的现代化,并使用户能够通过CC框架内的可插拔组件构建自定义缓存解决方案。CC是一个100%的Java解决方案,它广泛利用堆外内存,最大限度地减少了对Java垃圾收集(GC)的影响。

为什么用Carrot Cache?
像Caffeine和EHCache这样的传统缓存库很棒,但它们通常会带来挑战,特别是在扩展以处理大量数据集时。Carrot Cache旨在解决这些痛点:

1、增强的内存效率:与Caffeine或EHCache相比,我们的数据压缩提供了高达5倍的内存节省(参见“内存基准”部分)。这意味着您可以用更少的内存缓存更多的数据,从而降低基础架构成本。注意:在我们的基准测试中,我们使用Caffeine和EHCache的客户端数据压缩。

2.真正的数据分层:Carrot Cache支持混合存储,无缝利用RAM和SSD。这种方法超越了传统内存缓存的限制,并通过使用更便宜的SSD存储来降低成本。我们的磁盘引擎设计为低写入放大,在保持高吞吐量的同时保持SSD的寿命。

3.持久性和超快快照:缓存可以在应用程序关闭时保存,并在启动时恢复。数据快照性能比行业标准(Redis)快150倍,使备份和恢复操作几乎是即时的。此功能非常适合需要频繁数据备份或需要快速横向扩展的应用程序。

4.对象的过期支持:Carrot Cache API支持每个对象的过期时间(与Caffeine和EHCache不同),使其成为需要细粒度缓存控制的应用程序的理想选择,例如实时数据处理,会话管理和动态内容缓存。

5.最小元数据开销:不像其他系统为元数据招致显著的存储器开销(例如,对象索引、逐出跟踪),Carrot Cache保持元数据轻量级。这允许高效处理数十亿个数据对象,而不会消耗过多的内存。元数据开销取决于所使用的索引格式和所需的过期支持,但通常每个对象在8-14字节之间变化。要在缓存中保存10亿个对象,只需要8- 14 GB RAM。

6. SSD友好设计:Carrot Cache的独特架构确保仅顺序写入,最大限度地减少可能随时间降低SSD性能的随机I/O操作。这使其非常适合云环境和大规模数据工作负载。


特征

  • 多种操作模式:支持RAM-only,SSD-only,混合模式(RAM -SSD)和串联模式(RAM -压缩RAM)。
  • 高度可配置:用户可以自定义缓存准入策略(对SSD很重要)、提升策略(从牺牲缓存回到父缓存)、驱逐策略和吞吐量控制器。其他可定制的组件包括内存索引格式、内部GC回收选择器、数据写入器和数据读取器。
  • AI/ML就绪:自定义缓存准入和驱逐策略可以利用针对特定工作负载量身定制的复杂机器学习模型。
  • CacheGuard保护:将缓存准入策略与抗扫描缓存驱逐算法相结合,显著减少SSD磨损并延长使用寿命。
  • 低SSD写入放大(DWA)和缓存级写入放大(CLWA):通过持续的写入吞吐量设置进行控制。用户可以为特定SSD设备设置所需的持续写入吞吐量安全,系统动态调整参数以满足此要求。估计75% SSD使用的空间DLWA = 1.1,100% = 1.8。即使使用几乎满容量的SSD,写入也不会产生显著的DLWA。
  • 缓存项目的低RAM开销:RAM和SSD的开销范围为每个项目6-20字节,包括过期支持。开销取决于使用的索引格式。提供了几种索引格式,既有过期支持,也有没有过期支持。
  • RAM中的低Meta开销:示例如下:在CC中管理1 M数据项需要不到1 MB的Java堆和不到10 MB的Java堆外元数据内存。
  • 多种逐出算法:开箱即用,包括分段LRU(默认)、LRU和FIFO。分段LRU是一种抗扫描算法。驱逐政策是可插拔的,允许客户实现自己的。
  • 可扩展性:支持多TB的存储,最高可达256 TB,每个缓存项的RAM开销仅为11字节。
  • 有效的过期项目回收:专为需要过期支持的应用程序而设计。
  • 高度可配置:超过50个可配置参数。
  • 热重启:允许缓存数据在服务器完全重启后继续存在。数据保存和加载速度非常快,仅取决于可用的磁盘I/O吞吐量(每秒GB)。
  • 压缩:CC可以使用可插入的压缩编解码器实时压缩和重新编码键和值,从而显著减少内存使用。目前支持带字典的Zstd。
  • 兼容Memcached API:支持所有存储、检索和杂项命令(仅限文本协议)。

网友热评:
这帮人只算了两笔账:Java对象在"堆内"和"堆外"的内存开销。他们把数据像压缩饼干一样压成字节码,然后用内存地址直接定位。这样做出来的缓存结构特别节省空间,就像能把书包里的课本都换成电子书一样。虽然丢了课本的封面目录(元数据),打包解包(序列化)也挺费劲,但架不住一个书包能塞下更多书啊!

像微博、脸书、网飞这些天天被亿万人刷的APP,它们的缓存服务器必须保证99%的命中率——相当于你每天点外卖100次,最多只能有1次送错餐。所以它们宁可多买十个冰箱存菜(过度分配内存),也不在乎用什么策略管理食材。重点是怎么把大白菜腌成泡菜(紧凑存储),把过期食品直接扔掉(主动清理),这才是最近几年最流行的做法。

至于普通缓存(堆上缓存)?那完全是另一回事!就像你不能拿运货的擎天柱卡车和买菜用的丰田普锐斯比谁更省油——虽然都是车,但根本不是一个赛道的!比如有些场景,每次读缓存都要拆压缩包(反序列化),可能比直接查数据库还慢;还有些情况需要对象身份证(实例标识),这种需求堆外缓存就帮不上忙。

所以高手都是组合使用,像打游戏先L1缓存(内存)再L2缓存(硬盘)这样层层配合。