ehcacheh缓存
redis -> nosql 数据库
-> 分布式的缓存
1. 缓存简介
斐波那契
提高效率的一种方式, 将计算结果进行缓存
// key 是n value 是 fbnq(n)
static Map<Integer, Integer> map = new HashMap<>();
// 1 1 2 3 5 8 13 ...
// 递归实现
public static int fbnq(int n) {
if(n==1 || n==2) {
return 1;
}
// 获取n的 fbnq结果
Integer value = map.get(n);
if(value == null) {
// 首次执行, 将结果放入缓存
System.out.println("调用了fbnq..." + n);
value = fbnq(n - 1) + fbnq(n - 2);
map.put(n, value);
}
return value;
}
public static void main(String[] args) {
int fbnq = fbnq(7);
System.out.println(fbnq);
}
2. 缓存简单实现
如何实现一个 LRU (最近最少使用 元素,会优先从缓存中踢掉) 缓存
1 2 3 4 5 key
get(1)
2 3 4 5 1 再加入 6
3 4 5 1 6
LRU 是一种缓存淘汰算法:既会考虑元素放入缓存的时间先后(先放入的先淘汰),还会考虑元素的命中次数(命中次数少的应当优先淘汰)
利用 LinkedHashMap 来实现了简单的 LRU 缓存
// 默认模式: accessOrder=false 可以保持元素放入集合时的顺序
// accessOrder=true 可以根据访问(get方法)调整元素在集合中的顺序, 最新访问的排在最后
// 还要重写 removeEldestEntry 方法, 当方法返回为true 时, 表示要移除最老的元素
LinkedHashMap<String, String> map = new LinkedHashMap(16, 0.75f, true){
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
// 希望集合中放5个元素, 超过5个要移除最老的元素
if(this.size() <= 5) {
return false;
} else {
return true;
}
}
};
缓存分类:
- 对象缓存 (缓存的是一个个对象)
- 页面缓存 (缓存的是一张张页面)
3. 对象缓存
常见的缓存实现: ehcache, oscache, jboss cache
ehcache 2.x
ehcache 3.x (*)
3.1 ehcache 缓存
添加 maven 依赖
org.ehcache
ehcache
3.6.3
javax.cache
cache-api 添加配置文件-ehcache.xml
<?xml version=”1.0” encoding=”UTF-8”?>
java.lang.String
java.lang.String
200
创建 cacheManager 缓存管理器
- 获取 cache 对象(具体缓存)
存、取、移除元素
// 1. 类对象.getResource() 获取类路径上的一个资源文件(url对象)
URL url = GunsApplication.class.getResource(“/ehcache.xml”);
// 2. 准备配置对象
XmlConfiguration config = new XmlConfiguration(url);
// 3. 创建一个新的缓存管理器类
CacheManager cacheManager = CacheManagerBuilder.newCacheManager(config);// 4. 初始化缓存管理器
cacheManager.init();// 5. 获取缓存对象
Cachecache = cacheManager.getCache(“cache1”, String.class, String.class); // 6. 存储键值
cache.put(“beijing”, “北京”);// 7. 根据键获取值
System.out.println(cache.get(“beijing”));// 8. 删除键
cache.remove(“beijing”);
System.out.println(cache.get(“beijing”));// 9. 清空缓存
cache.clear();
3.2 与 springboot 整合
spring:
cache:
jcache:
config: ehcache.xml
spring.cache.jcache.config=ehcache.xml
@EnableCaching -- 启用缓存功能(内部就会创建 cacheManager(spring中), 并交给spring容器管理)
在其他需要用到缓存的代码中,就可以利用 cacheManager,来使用缓存,例如:
@Autowired
private CacheManager cacheManager;
public Dept detail(@PathVariable("deptId") Long deptId) {
Cache cache = cacheManager.getCache("cache1");
Cache.ValueWrapper value = cache.get(String.valueOf(deptId));
if(value != null) {
Dept dept = (Dept) value.get();
return dept;
} else {
Dept dept = deptService.getById(deptId);
cache.put(String.valueOf(deptId), dept);
return dept;
}
}
上面的代码虽然实现了缓存功能,但发现有太多重复代码,所以利用aop思想改造它。
spring 已经把缓存的切面类写好了,我们只需要通过一些注解标记哪些方法需要缓存
- @Cacheable 加在需要将结果进行缓存的方法上
执行流程:
/** * 1) 请求过来, 先访问的是 DeptController 的代理对象 (cglib 生成 DeptController 的子类对象作为代理对象) * 2) 代理对象重写了 detail, 在 detail 方法内, 通过 缓存管理器 找到名为 cache2 的缓存 * 3) Cache.get(deptId) 去获取 value, 第一次访问 value 为空, 放行执行真正的 DeptController 的 detail 方法 * 4) 真正的 DeptController 的 detail 方法返回结果作为 value 放入 cache2 缓存 * * 5) 第二次请求过来, 先访问的是 DeptController 的代理对象 * 6) 代理对象重写了 detail, 在 detail 方法内, 通过 缓存管理器 找到名为 cache2 的缓存 * 7) Cache.get(deptId) 去获取 value, 返回不为空, 直接返回缓存中的 value, 没有执行真正的 DeptController 的 detail 方法 */
@RequestMapping(value = "/detail/{deptId}")
@ResponseBody
@Cacheable("cache2")
public Dept detail(@PathVariable("deptId") Long deptId) {
return deptService.getById(deptId);
}
@CacheEvict - 用来删除某个key value,或者清空整个缓存, 用在执行了数据的增删改时,这三种情况下,都应该让缓存失效
- 有时,需要将整个缓存清空, 这时使用 @CacheEvict(allEntries=true)
- @CachePut - 也是加在增删改方法之上,执行增删改方法,把方法的返回值当做value,用它更新缓存的内容
key 的生成,默认使用的方法参数作为key,但容易产生key冲突问题
cache2 /dept/detail/30 30->Dept
cache3 /user/detail/30 30->User// 30 dept30
// 29 dept_29
// #deptId 就是在方法参数中找一个名为 deptId 的参数, 取得值后前面拼接 一个 ‘dept‘ 字符串, 拼接后的结果作为 key
// 这种写法称之为 spring 表达式, SPEL (spring expression language)
@Cacheable(cacheNames = “cache2”, key = “‘dept_’ + #deptId”)
压测工具 jmeter(java语言编写的)
缓存的使用场景
- 提高系统吞吐量的有效手段
- 经常查询,但很少修改的数据,需要配置缓存,经常修改的数据,不要配置缓存,反而会影响增删改效率 (读多写少)
- ehcache 经常用于本地缓存(单机)
- 如果数据要被多台机器共享访问,需要用到分布式缓存 redis
要注意数据一致性问题
- 考虑性能,不用加锁,能够保证最终一致性即可(中间会有一小段时间读取到旧数据)
- 对一致性要求高,加锁,但性能会受到影响
4. 页面静态化
把页面从动态页面,变为静态页面(静态页面会具备各种各样的缓存功能)
之前配置 springmvc.xml
<beans>
<!-- 视图解析器 -->
<mvc:view-resolvers>
<mvc:jsp prefix="" suffiex=""/>
</mvc:view-resolvers>
<!-- 拦截器 -->
<!-- 静态资源配置 -->
<mvc:default-servlet-handler/>
<!-- 上传文件解析器 -->
<mvc:resources mapping="url路径" location="服务器磁盘路径"/> <!-- 把一个web路径映射到服务器上的一个静态资源 -->
</beans>
通过java 类配置
@Configuration // 表示他是一个配置类
public class MyWebConfig implements WebMvcConfigurer { // 这个接口中有一些抽象方法,分别对应xml中的各项配置
// <!-- 静态资源配置 -->
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// <!-- 拦截器 -->
public void addInterceptors(InterceptorRegistry registry) {
}
...
}
4.1 浏览器缓存
// 最强,离最终使用者越近,缓存带来的性能提升越高
// 调试快捷键 f5-普通刷新 ctrl+f5-强制刷新
- 第一次请求,从服务器得到了一个静态页面,把静态页面放入浏览器的缓存
- 后续访问这个页面,不会发送请求给服务器,直接从浏览器的缓存中获取这个静态页面
- 点击 f5 刷新,仍然会从服务器获取
- html 缓存有一个 max-age的最大存活期,超过了这个时间就会绕过缓存去找服务器了
4.2 cdn 缓存
网络请求静态资源时,常常不需要真正到达最终服务器,会找到一个离自己最近的cdn服务器,由cdn服务器返回结果
cdn中缓存的大部分都是静态资源(图片,html等)
4.3 304
请求确实发送给服务器了,但服务器发现网页并没有发生变化,返回一个304状态码(告诉浏览器,这个网页没有被修改,请从自己缓存中获取网页),网页的正文并没有传输
4.4 localstorage
html5 引入的一个js对象
服务器如果返回的是json, 希望缓存这些数据的话,可以将其存入 localStorage 中,即使浏览器关闭,localStorage中的内容仍然存在
还没有评论,来说两句吧...