转载请标明出处:
原文首发于: http://www.zhangruibin.com
本文出自 RebornChang的博客
说数据存储,抛开数据库持久化存储手段oracle,MySQL等,再抛开非持久化数据库redis等,再刨除磁盘存储,本章简单说下catch里面的主流:GuavaCache Memcache Ehcache。
GuavaCache Memcache Ehcache简介
GuavaCache
1.封装了get、put操作,能够集成数据源。一般我们在业务中操作缓存都会操作缓存和数据源两部分。例如:put数据时,先插入DB再删除原来的缓存,get数据时,先查缓存,命中则返回,没有命中时需要查询DB,再把查询结果放入缓存中。Guava封装了这么多步骤,只需要调用一次get/put方法即可。
2. 是线程安全的缓存,与ConcurrentMap类似,但前者增加了更多的元素失效策略,后者只能显示的移除元素。
3. GuavaCache支持四种回收方式: (1)基于容量回收(Size-based Eviction);(2)基于时间回收(Timed Eviction);(3)基于引用类型的回收Reference-based Eviction);(4)手动回收。
4. 它可以监控加载/命中情况。
Memcache
memcache是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负担。它通过在内存中缓存数据和对象,来减少读取数据库的次数。从而提高动态、数据库驱动网站速度。
memcache通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。memcache主要用于分担数据库负的压力,memcache将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。
EHCache
EHCache是来自sourceforge(http://ehcache.sourceforge.net/) 的开源项目,也是纯Java实现的简单、快速的Cache组件。EHCache支持内存和磁盘的缓存,支持LRU、LFU和FIFO多种淘汰算法,支持分 布式的Cache,可以作为Hibernate的缓存插件。同时它也能提供基于Filter的Cache,该Filter可以缓存响应的内容并采用 Gzip压缩提高响应速度。
缓存优劣的比较点
过期时间
容量规划(重要)
清除策略(重要)
命中率统计
性能比较- Memcache Ehcache
项目 | Memcache | Ehcache |
分布式 | 不完全,集群默认不实现 | 支持 |
集群 | 可通过客户端实现 | 支持(默认是异步同步) |
持久化 | 可通过第三方应用实现 | 支持。持久化到本地硬盘,生成一个.data和.index文件。cache初始化时会自动查找这两个文件,将数据放入cache |
效率 | 高 | 高于Memcache |
容灾 | 可通过客户端实现。 | 支持 |
缓存数据方式 | 缓存在memcached server向系统申请的内存中 | 可以缓存在内存(JVM中),也可以缓存在硬盘。通过CacheManager管理cache。 |
缓存过期移除策略 | LRU | LRU(默认),FIFO,LFU |
缺点 | 功能不完善,相对于Ehcache效率低 | 只适用于java体系,只能用java编写客户端 |
优点 | 简洁,灵活,跨平台 | 效率高,功能强大。 |
名词解释
缓存移除策略(三种常见):
FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;
LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;
LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除。
Expirations有三种方式:
1.永不失效:Expirations.noExpiration();
2. TTL(Time To Live):存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期);
例子
:Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.MINUTES));
3. TTI(Time To Idle):空闲期,即一个数据多久没被访问将从缓存中移除的时间。
例子:
Expirations.timeToIdleExpiration(Duration.of(5, TimeUnit.MINUTES));
另外,Ehcache 中还可以通过自行实现Expiry接口来自定义缓存失效策略。
GuavaCache使用
1.maven添加依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
2.代码层 GuavaCacheTest .java
package com.example.demo.gitBooks.catchAnalysis.guavaCacheAnalysis;
import com.example.entity.User;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/*
* @ClassName GuavaCacheTest
*@Description TODO
*@Author zhangrui
*@Date 14:04 14:04
*@Version
* */
public class GuavaCacheTest {
private static Logger logger = LoggerFactory.getLogger(GuavaCacheTest.class);
public static void main(String[] args) {
Cache<Object,Object> cache = CacheBuilder
.newBuilder()
//设置并发级别
.concurrencyLevel(10)
//设置缓存有效时间,超时的话会被回收
.expireAfterAccess(10, TimeUnit.MINUTES)
//设置缓存条数
.maximumSize(100)
.build();
final Integer userId = 10001;
try {
//
User user = (User)cache.get(userId, new Callable<User>() {
@Override
public User call() throws Exception {
return getUserInfoByUserId(userId);
}
});
System.out.println(user.getName() == null?"没有獲取到了用戶":"獲取到了用戶:"+user);
logger.info("执行上述成功!!");
} catch (ExecutionException e) {
e.printStackTrace();
logger.info("抛出了异常!!");
}
}
public static User getUserInfoByUserId(Integer userAge) {
User user1 = new User("张三", 10001);
User user2 = new User("李四", 10002);
User user3 = new User("王五", 10003);
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
User user = new User();
for (User userTemp : userList){
if (userTemp.getAge() == userAge){
user = userTemp;
}
}
return user;
}
}
Ehcache 的使用
1.maven添加依赖
<!--ehcache缓存-->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.3.1</version>
</dependency>
代码层
EhcacheTest.java
import com.example.entity.User;
import org.ehcache.Cache;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.CacheManagerConfiguration;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;
import java.io.File;
import java.util.concurrent.TimeUnit;
/*
* @ClassName EhcacheTest
*@Description TODO 同时初始化堆缓存,堆外缓存,磁盘缓存,
* TODO 其中:heap + offheap 的组合,offheap 就是 Authoritative Tier,heap,就是 Caching Tier;
*TODO 对于heap + offheap + disk 的组合,disk 层就是 Authoritative Tier,heap + offheap 就是 CachingTier。
*@Author zhangrui
*@Date 10:47 10:47
*@Version
* */
public class EhcacheTest {
public static void main(String[] args) {
//声明持久化(persistence)的磁盘位置为g盘
//TODO 在Linux下需要修改
CacheManagerConfiguration<PersistentCacheManager> persistentManagerConfig = CacheManagerBuilder
.persistence(new File("g:\\", "ehcache-test"));
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(persistentManagerConfig).build();
// 手动初始化
persistentCacheManager.init();
// 空间大小 heap < offheap < disk,否则会报错java.lang.IllegalArgumentException
//第三个参数设置为true,支持持久化
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(100, MemoryUnit.MB)
.disk(500, MemoryUnit.MB, true);
//写入缓存
CacheConfiguration<Long, User> config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, User.class, resource)
//设置Caching Tier缓存有效时间,因为存入缓存是直接存到比 AuthoritativeTier中,所以,Caching Tier失效也不会影响数据
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(10, TimeUnit.SECONDS)))
.build();
Cache<Long, User> cache = persistentCacheManager.createCache("userInfoCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(config));
//写入缓存
cache.put(10001L, new User("张三", 1));
cache.put(10002L, new User("李四", 2));
cache.put(10003L, new User("王五", 3));
//读取缓存,如果要读取的是未存放放的,不会抛出异常,返回为null
User user1 = new User();
user1 = cache.get(10001L);
User user2 = new User();
user2 = cache.get(10002L);
User user3 = new User();
user3 = cache.get(10003L);
//输出缓存
System.out.println(user1 == null ? "获取失败,以为没有成功放入缓存,其实是堆溢出清除!!" : "成功的获取了缓存:" + "**************" + user1 + "***********");
System.out.println("成功的获取了缓存:" + "**************" + user2 + "***********");
System.out.println(user3 == null ? "获取失败,以为没有成功放入缓存,可能的原因为声明的heap值过小!!" : "成功的获取了缓存:" + "**************" + user3 + "***********");
// 再程序关闭前,需要手动释放资源
persistentCacheManager.close();
}
}
CacheManagerTest.java
import com.example.entity.User;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
/*
* @ClassName CacheManagerTest
*@Description TODO 声明的缓存heap大小是一定的,当后续加入缓存区满了之后,再继续加入的话则会遵从栈存储原则,堆底元素出堆消亡
*@Author zhangrui
*@Date 10:47 10:47
*@Version
* */
public class CacheManagerTest {
public static void main(String[] args) {
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
//手动初始化
cacheManager.init();
ResourcePoolsBuilder resourcePoolsBuilder = ResourcePoolsBuilder.heap(2);
//1.按照存储的条目数进行初始化
ResourcePoolsBuilder resourcePoolsBuilder1 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2, EntryUnit.ENTRIES);
//2.按照存储的条目数进行初始化的简写方式
ResourcePoolsBuilder resourcePoolsBuilder2 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2);
//3.按照存储空间大小初始化
ResourcePoolsBuilder resourcePoolsBuilder3 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2, MemoryUnit.GB);
CacheConfiguration<Integer, User> configuration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, User.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.KB)
.offheap(10, MemoryUnit.MB))
.withSizeOfMaxObjectGraph(3)
.withSizeOfMaxObjectSize(4, MemoryUnit.KB)
.build();
//创建缓存对象
Cache<Integer, User> cache = cacheManager.createCache("userInfo", CacheConfigurationBuilder.newCacheConfigurationBuilder(configuration));
//写入缓存
cache.put(10001, new User("张三", 1));
cache.put(10002, new User("李四", 2));
cache.put(10003, new User("王五",3));
//读取缓存
User user1 = new User();
user1 = cache.get(10001);
User user2 = new User();
user2 = cache.get(10002);
User user3 = new User();
user3 = cache.get(10003);
//输出缓存
System.out.println(user1 == null ? "获取失败,以为没有成功放入缓存,其实是堆溢出清除!!" : "成功的获取了缓存:" + "**************" + user1 + "***********");
System.out.println("成功的获取了缓存:" + "**************" + user2 + "***********");
System.out.println(user3 == null ? "获取失败,以为没有成功放入缓存,可能的原因为声明的heap值过小!!" : "成功的获取了缓存:" + "**************" + user3 + "***********");
//再程序关闭前,需要手动释放资源
//不用手动removeCache,根据打印日志,会自动清除;
cacheManager.removeCache("userInfo");
cacheManager.close();
}
}
PS:
1.memcache.需要配置服务端安装什么的,博主嫌繁琐,就没弄,所以这里没代码展示。
2.本博文代码都已经上传到了博主的GitHub上,代码地址:
RebornChang’s GitHub
3.本博文部分资源摘自网络,若有雷同,请见谅。
Over!