一 缓存简介
首先你要明白一点的是:我们应用程序中的数据要进行永久性存储,要么存于文件,要么存于数据库,
那么不管是文件还是数据库,其实都在磁盘上。问题来了,磁盘是低速存储设备,如果我们要从磁盘读取数据,
性能将会很差,当然这个很差是相对于内存来说,内存的读写性能通常是磁盘的10倍以上!
所以如果我们把应用程序和磁盘的数据交换变为和内存的数据交换,将大大提高应用程序的性能!
缓存:即让数据更接近于使用者;
工作机制是:先从缓存中(内存中)读取数据,如果没有再从慢速设备(磁盘)上读取实际数据(数据也会存入缓存);
缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/IO)的且对于相同的请求有相同的计算结果的数据。
例如:我们一个查询学生数据的web应用程序,我们先让它在内存中找,如果没有就去到磁盘上找。
还有如用过Maven的朋友都应该知道,我们找依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器找;
还有如京东的物流为什么那么快?
他们在各个地都有分仓库,如果该仓库有货物那么送货的速度是非常快的。
如果分库没有,那就只能让全国的集散中心发货,那就比较慢了!
缓存命中率
即从缓存中读取数据的次数 与 总读取次数的比率,命中率越高越好:
命中率 = 从缓存中读取次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])
Miss率 = 没有从缓存中读取的次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])
这是一个非常重要的监控指标,如果做缓存一定要健康这个指标来看缓存是否工作良好;
缓存策略
Eviction policy
移除策略,即如果缓存满了,从缓存中移除数据的策略;常见的有LFU、LRU、FIFO:
FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;
LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;
LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除;
TTL(Time To Live )
存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期)
TTI(Time To Idle)
空闲期,即一个数据多久没被访问将从缓存中移除的时间。
到此,基本了解了缓存的知识。
在Java中,我们一般对调用方法进行缓存控制,比如我调用"findUserById(Long id)",
那么我应该在调用这个方法之前先从缓存中查找有没有,如果没有再调用该方法如从数据库加载用户,
然后添加到缓存中,下次调用时将会从缓存中获取到数据。
二 java web程序中使用缓存
java web程序中如果要使用缓存,一般有两种方案:1. 使用hibernate框架结合缓存插件(ehcache,oscache) 或使用mybatis自带的缓存机制;
2. 使用独立缓存程序(redis,memcached等)。本文使用第一种,独立缓存的使用请继续关注我的博文。
这里我们使用mybatis自带的缓存机制来讲解该问题。关于mybatis缓存请参考我的博文【http://blog.csdn.net/wx5040257/article/details/78768244】
假如有一个查询员工的web程序,如图所示:
如果没有利用缓存,那么反复点击首页,就会反复查询数据库,从日志输出中,我们可以看到每次都会输出sql语句
好,现在我们把缓存功能加上
首先在mybatis核心配置文件中加入如下代码开启缓存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 是否开启全局缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<setting name="lazyLoadingEnabled" value="false"/>
<!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
<typeAliases>
<typeAlias type="com.obtk.entitys.StudentEntity" alias="StudentEntity"/>
<typeAlias type="com.obtk.entitys.DeptEntity" alias="DeptEntity"/>
<typeAlias type="com.obtk.entitys.UserEntity" alias="UserEntity"/>
<typeAlias type="com.obtk.entitys.PageEntity" alias="PageEntity"/>
</typeAliases>
</configuration>
注意settings部分!
然后在mapper文件中加入如下代码
接下来重新发布应用程序,继续多次点击首页,观察结果
有缓存命中率,说明缓存命中了,并没有看到sql语句,说明数据来自内存!
接下来删除一条数据
然后继续点击第一页,发现又有sql输出了,说明删除数据的时候缓存被刷新了(清除了)!
进行一下登录操作
没有登录sql,说明用户操作利用了缓存!
说明刷新缓存只刷新当前mapper文件的缓存数据,而不会影响其它mapper文件的缓存数据!
这里讲的就是集成式缓存,但这种缓存方案不适合大型应用,因为应用程序和缓存共用同一块内存,数据量大的话会导致内存不足!
请继续关注我的博文,后面将会讲解独立缓存的使用!!