我們的項目引入本地緩存無冕之王—Caffeine
前言
大家好,我是田螺。
最近給我的知識星球手把手書城系統,引入了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;
}