1.簡(jiǎn)介
? ? ? ?Caffeine 是基于 JAVA 8 的高性能緩存庫(kù)。并且在 spring5 (springboot 2.x) 后,spring 官方放棄了 Guava,而使用了性能更優(yōu)秀的 Caffeine 作為默認(rèn)緩存組件。
? ? ? ?Caffeine 因?yàn)槭褂昧?Window-TinyLFU?緩存淘汰策略,提供了一個(gè)近乎最佳的命中率。綜合了?LRU?和 LFU 算法的長(zhǎng)處,使其成為本地緩存之王。
? ? ? ? Least Recently Used:如果數(shù)據(jù)最近被訪(fǎng)問(wèn)過(guò),將來(lái)被訪(fǎng)問(wèn)的概率也更高。每次訪(fǎng)問(wèn)就把這個(gè)元素放到隊(duì)列的頭部,隊(duì)列滿(mǎn)了就淘汰隊(duì)列尾部的數(shù)據(jù),即淘汰最長(zhǎng)時(shí)間沒(méi)有被訪(fǎng)問(wèn)的。缺點(diǎn)是,如果某一時(shí)刻大量數(shù)據(jù)到來(lái),很容易將熱點(diǎn)數(shù)據(jù)擠出緩存,留下來(lái)的很可能是只訪(fǎng)問(wèn)一次,今后不會(huì)再訪(fǎng)問(wèn)的或頻率極低的數(shù)據(jù)。比如、微博爆出某明星糗事就是一個(gè)突發(fā)性熱點(diǎn)事件。當(dāng)事件結(jié)束后,可能沒(méi)有啥訪(fǎng)問(wèn)量了,但是由于其極高的訪(fǎng)問(wèn)頻率,導(dǎo)致其在未來(lái)很長(zhǎng)一段時(shí)間內(nèi)都不會(huì)被淘汰掉。
? ? ? ? Least Frequently Used:如果數(shù)據(jù)最近被訪(fǎng)問(wèn)過(guò),那么將來(lái)被訪(fǎng)問(wèn)的概率也更高。也就是淘汰一定時(shí)間內(nèi)被訪(fǎng)問(wèn)次數(shù)最少的數(shù)據(jù)(時(shí)間局部性原理)需要用 Queue 來(lái)保存訪(fǎng)問(wèn)記錄,可以用 LinkedHashMap 來(lái)簡(jiǎn)單實(shí)現(xiàn)一個(gè)基于 LRU 算法的緩存。優(yōu)點(diǎn)是,避免了 LRU 的缺點(diǎn),因?yàn)楦鶕?jù)頻率淘汰,不會(huì)出現(xiàn)大量進(jìn)來(lái)的擠壓掉老的,如果在數(shù)據(jù)的訪(fǎng)問(wèn)的模式不隨時(shí)間變化時(shí)候,LFU 能夠提供絕佳的命中率。其缺點(diǎn)是,偶發(fā)性的、周期性的批量操作會(huì)導(dǎo)致LRU命中率急劇下降,緩存污染情況比較嚴(yán)重。
? ? ? ?W-TinyLFU 是 Caffeine 提出的一種全新算法,它可以解決頻率統(tǒng)計(jì)不準(zhǔn)確以及訪(fǎng)問(wèn)頻率衰減的問(wèn)題。這個(gè)方法讓我們從空間、效率、以及適配舉證的長(zhǎng)寬引起的哈希碰撞的錯(cuò)誤率上做均衡。在 W-TinyLFU 算法中,將整個(gè)緩存區(qū)劃分為兩大區(qū)域:Window Cache
?和?Main Cache
?。Window Cache 是一個(gè)標(biāo)準(zhǔn)的 LRU 緩存,只占整個(gè)緩存內(nèi)存空間大小的 1% ;?Main Cache 則是一個(gè) SLRU (Segmented LRU) ,占整個(gè)緩存內(nèi)存空間大小的 99% ,是緩存的主要區(qū)域。里面進(jìn)一步被劃分成兩個(gè)區(qū)域:Probation Cache 觀察區(qū)和 Protected Cache 保護(hù)區(qū)。Probation 觀察區(qū)占 Main Cache 大小的 20%;而 Protected 保護(hù)區(qū)占 Main Cache 大小的 80% ,是 Main Cache 的主要區(qū)域。
2.caffeine相關(guān)配置說(shuō)明
參數(shù) | 類(lèi)型 | 描述 |
---|---|---|
initialCapacity | integer | 初始的緩存空間大小 |
maximumSize | long | 緩存的最大條數(shù) |
maximumWeight | long | 緩存的最大權(quán)重 |
expireAfterAccess | duration | 最后一次寫(xiě)入或訪(fǎng)問(wèn)后,指定經(jīng)過(guò)多長(zhǎng)的時(shí)間過(guò)期 |
expireAfterWrite | duration | 最后一次寫(xiě)入后,指定經(jīng)過(guò)多長(zhǎng)的時(shí)間緩存過(guò)期 |
refreshAfterWrite | duration | 創(chuàng)建緩存或者最近一次更新緩存后,經(jīng)過(guò)指定的時(shí)間間隔后刷新緩存 |
weakKeys | boolean | 打開(kāi) key 的弱引用 |
weakValues | boolean | 打開(kāi) value 的弱引用 |
softValues | boolean | 打開(kāi) value 的軟引用 |
recordStats | - | 開(kāi)發(fā)統(tǒng)計(jì)功能 |
Caffeine提供三種數(shù)據(jù)驅(qū)逐策略:基于大小驅(qū)逐、基于時(shí)間驅(qū)逐、基于引用驅(qū)逐。
- maximumSize?和?maximumWeight?不可以同時(shí)使用。
- expireAfterWrite?和?expireAfterAccess?同時(shí)存在時(shí),以?expireAfterWrite?為準(zhǔn)
- weakValues?和?softValues?不可以同時(shí)使用。
Java中四種引用類(lèi)型:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-774471.html
引用類(lèi)型 | 被垃圾回收時(shí)間 | 用途 | 生存時(shí)間 |
---|---|---|---|
強(qiáng)引用 Strong Reference | 從來(lái)不會(huì) | 對(duì)象的一般狀態(tài) | JVM停止運(yùn)行時(shí)終止 |
軟引用 Soft Reference | 在內(nèi)存不足時(shí) | 對(duì)象緩存 | 內(nèi)存不足時(shí)終止 |
弱引用 Weak Reference | 在垃圾回收時(shí) | 對(duì)象緩存 | gc運(yùn)行后終止 |
虛引用 Phantom Reference | 從來(lái)不會(huì) | 可以用虛引用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng),當(dāng)一個(gè)虛引用關(guān)聯(lián)的對(duì)象被垃圾收集器回收之前會(huì)收到一條系統(tǒng)通知 | JVM停止運(yùn)行時(shí)終止 |
3.springboot集成caffeine
- 直接引入 Caffeine 依賴(lài),然后創(chuàng)建?Caffeine 方法實(shí)現(xiàn)緩存。
- 引入 Caffeine 和 SpringCache 依賴(lài),使用 SpringCache 注解方法實(shí)現(xiàn)緩存。(推薦)
- 引入jar包 版本也可根據(jù)不寫(xiě) 根據(jù)springboot版本走
<!-- caffeine本地緩存 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.5</version>
</dependency>
<!-- 對(duì)于@EnableCaching注解開(kāi)啟-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
- @Cacheable:先查詢(xún)緩存是否存在,如果存在,則返回結(jié)果;如果不存在,則執(zhí)行方法,并將結(jié)果保存在緩存中。主要用于查詢(xún)操作
- @CachePut:不檢查緩存中是否存在,直接執(zhí)行方法,并將結(jié)果保存在緩存中。主要用于數(shù)據(jù)新增和修改操作
- @CacheEvict:從緩存中移除指定的key數(shù)據(jù),如果allEntries屬性為true,則清除全部;如果beforeInvocation屬性為true,則在方法執(zhí)行前刪除。主要用于刪除操作
3.1 方式一 只用Caffeine
/**
* caffeine緩存配置
* - 優(yōu)點(diǎn):可以針對(duì)每個(gè)cache配置不同的參數(shù),比如過(guò)期時(shí)長(zhǎng)、最大容量(定制化配置)
* 可定義多個(gè)
**/
@Configuration
public class CaffeineConfig {
@Bean
public Cache<Long, ItemStock> userDtoCache() {
// 構(gòu)建cache對(duì)象
return Caffeine.newBuilder()
//0) 驅(qū)逐策略:基于容量,時(shí)間,引用。
//0.1 基于時(shí)間
.expireAfterWrite(10, TimeUnit.MINUTES)
//0.2.1 基于容量
//初始容量
.initialCapacity(100)
.maximumSize(10_000)
// //0.2.2 權(quán)重
// .weigher(((key, value) -> {
// if (key.equals(1)) {
// return 1;
// } else {
// return 2;
// }
// }))
//0.3 基于引用
//0.3.1 當(dāng)進(jìn)行GC的時(shí)候進(jìn)行驅(qū)逐
// .softValues()
//0.3.2 當(dāng)key和緩存元素都不再存在其他強(qiáng)引用的時(shí)候驅(qū)逐
// .weakKeys()
// .weakValues()
.build();
}
測(cè)試類(lèi)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-774471.html
@SpringBootTest(classes = CaffeineApplication.class)
@RunWith(SpringRunner.class)
public class CaffeineTest{
@Autowired
private Cache<Long, UserDto> userDtoCache;
@Test
public void test1() {
//1) 新增
// 添加或者更新一個(gè)緩存元素
userDtoCache.put(1L, UserDto.builder()
.id(1L)
.name("迪麗熱巴")
.age(18)
.build());
// 查找緩存,如果緩存不存在則生成緩存元素, 如果無(wú)法生成則返回null
UserDto user2 = userDtoCache.get(2L, key -> {
// 根據(jù)key去數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù)
return UserDto.builder()
.id(key)
.name("柳巖")
.age(24)
.build();
});
//2) 查詢(xún)
// 取數(shù)據(jù),上面的get也算
UserDto user1 = userDtoCache.getIfPresent(1L);
System.out.println("user1 = " + user1);
System.out.println("user2 = " + user2);
//3) 刪除
// 移除一個(gè)緩存元素
userDtoCache.invalidate(1L);
user1 = userDtoCache.getIfPresent(1L);
System.out.println("user1 = " + user1);
// 批量失效key
userDtoCache.invalidateAll(Arrays.asList(1L, 2L));
// 失效所有的key
userDtoCache.invalidateAll();
System.out.println("defaultGF2 = " + userDtoCache.getIfPresent("defaultGF"));
}
3.2 加入SpringCache 通過(guò)注解方式
@Configuration
@EnableCaching
public class CaffeineConfig {
/**
* 配置緩存管理器
*
* @return 緩存管理器
*/
@Bean("caffeineCacheManager")
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
// 設(shè)置最后一次寫(xiě)入或訪(fǎng)問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期
// .expireAfterAccess(60, TimeUnit.SECONDS)
// 初始的緩存空間大小
.initialCapacity(100)
// 緩存的最大條數(shù)
.maximumSize(1000));
return cacheManager;
}
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Cacheable(key = "#id" , value = "caffeineCacheManager")
public User selectById(Long id) {
User user = userMapper.selectById(id);
return user;
}
@Override
@CachePut(key = "#result.id" , value = "caffeineCacheManager")
public User addUserInfo(UserDto userDto) {
User user = new User();
BeanUtils.copyProperties(userDto, user);
int insert = userMapper.insert(user);
return user;
}
@Override
@CachePut(key = "#userDto.id" , value = "caffeineCacheManager")
public User updateUserInfo(UserDto userDto) {
User user = new User();
BeanUtils.copyProperties(userDto, user);
userMapper.updateById(user);
return user;
}
@Override
@CacheEvict(key = "#id" , value = "caffeineCacheManager")
public void deleteById(Long id) {
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("id", id);
userMapper.delete(updateWrapper);
}
}
到了這里,關(guān)于springboot 集成caffeine單體緩存兩種方式及算法簡(jiǎn)介 (注解/手動(dòng))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!