前言
大家好,我是田螺。
最近给我的知识星球手把手书城系统,引入了Caffeine本地缓存。看网上评价,它居然被称为本地缓存无冕之王,本文给大家简单介绍一下它吧~~
- Caffeine 是什么?
- Caffeine的优点与缺点
- 实际业务的使用场景
- 使用Caffeine需要注意的问题
- Caffeine与Redis的区别及选择
- 代码实践实现
1. Caffeine 是什么?
com.github.benmanes.caffeine.cache.Caffeine.Caffeine 是一个由Google开源的高性能Java本地缓存库。它提供了灵活的配置选项和强大的缓存淘汰策略,旨在满足现代应用程序对低延迟和高吞吐量的需求。Caffeine通过精细的锁优化和无锁数据结构,实现了极高的并发性能。
简单一句话概括,就是Caffeine是具有高性能的Java本地缓存库。
2. Caffeine的优点与缺点
优点:
- 高性能:使用基于 ConcurrentHashMap 的非阻塞算法,保证了并发情况下的高性能。
- 灵活配置:支持多种缓存淘汰策略,如LRU、LFU等,并允许自定义缓存大小、过期时间等参数。
- 丰富的统计信息:内置统计功能,帮助开发者深入了解缓存的行为,从而进行针对性的优化。
缺点:
- 本地缓存限制:由于Caffeine是本地缓存,因此无法跨进程共享数据,这在分布式系统中可能是一个限制。
- 仅适合短期缓存:不提供持久化机制,重启后缓存会丢失。
- 缓存大小限制:Caffeine 适用于本地缓存场景,缓存大小受制于 JVM 可用内存。
3. 实际业务的使用场景
有哪些场景适合使用它呢?
系统需要频繁查的,数据变化频率较低。
比如:
- 用户信息缓存:将用户的基本信息(如用户名、角色、权限)缓存到本地。
- 配置项缓存:存储系统的静态配置信息(如字典表、产品分类信息)。
- 热门内容缓存:缓存热门商品、推荐内容或排行榜,避免频繁从数据库中读取。
我们项目组,当前实际开发的项目,产品配置参数表,就使用了本地缓存。
4. 使用Caffeine需要注意的问题
- 缓存一致性:在分布式系统中,需要确保多个节点之间的缓存一致性。
- 内存管理:合理配置缓存大小和淘汰策略,避免内存溢出和性能下降。同时,定期监控缓存使用情况,及时调整配置。
- 缓存击穿:对于某些热点数据,如果缓存失效且访问量巨大,可能会导致数据库压力骤增。可以通过设置热点数据的永不过期策略或使用互斥锁来避免缓存击穿。(如果只是单纯用来做缓存配置表这些,则不用担心这个问题)
5. Caffeine与Redis的区别及选择
- 存储位置:Caffeine是本地缓存,存储在JVM内存中;而Redis是远程缓存,存储在内存中,但可以通过网络访问。
- 性能:由于Caffeine是本地缓存,访问速度更快,延迟更低;而Redis虽然性能也很高,但受限于网络延迟。
- 持久化:Redis支持数据持久化,即使服务器重启也能恢复数据;而Caffeine是内存缓存,数据在服务器重启后会丢失。
- 分布式:Redis是分布式缓存,支持多个节点之间的数据同步和共享;而Caffeine是本地缓存,无法跨进程共享数据。
我们在哪些场景,选择哪种缓存呢?
- 对于需要高性能和低延迟的应用:如果数据量不大且不需要持久化,可以选择Caffeine作为本地缓存。
- 对于需要跨进程共享数据的应用:如果数据量较大且需要持久化或跨进程共享,可以选择Redis作为远程缓存。
其实,实际开发中,我们都是结合两者一起使用的。
它两还可以实现多级缓存策略。例如,将热点数据缓存到Caffeine中,以提高访问速度;将非热点数据缓存到Redis中,以实现跨进程共享和持久化。 这个合理嘛?热点数据不可以放redis?
6. 代码实现
1)先引入maven包:
<!-- Spring Boot Starter Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Caffeine Cache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
2)实现CacheConfig缓存配置管理器
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("book");
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.maximumSize(500) // 设置缓存的最大容量
.expireAfterWrite(10, TimeUnit.MINUTES); // 设置写入后过期时间
}
}
3)在需要添加缓存的地方加个注解即可,很简单
@Override
@Cacheable("book")
public BookVO queryBookById(Integer bookId) {
BookPO bookPO = bookRepository.queryBookById(bookId);
BookVO bookVO = new BookVO();
BeanUtils.copyProperties(bookPO, bookVO);
log.info("查询书本:{}", bookId);
return bookVO;
}