02-Mybatis-08-查询缓存-一级和二级缓存

1. 一级缓存

  • 查询缓存的作用:主要是为了提高查询访问速度。将用户对用一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。
    查询缓存
  • MyBatis的查询缓存机制,根据缓冲区的作用域与生命周期,可划分为两种:一级缓存与二级缓存;
  • MyBatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同namespace的mapper查询数据存放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的;
  • 但一、二级缓存的不同之处在于:SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不覆存在。而二级缓存的生命周期会与整个应用同步,与SqlSesson是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSesson)间共享数据,而二级缓存是在不用线程(不同的SqlSesson)间共享数据——生命周期

证明一级缓存是存在的

// 证明从一级缓存是存在的
	@Test
	public void testSelectStudentByName() {
		Student student = dao.selectStudentById(2);
		System.out.println(student);
		
		Student student2 = dao.selectStudentById(2);
		System.out.println(student2);
	}
[DEBUG] ==>  Preparing: select tid id,tname name,tage age,score from student where tid=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
Student [id=2, name=王五, age=23, score=93.5]
  • MyBatis一级缓存是基于org.apache.ibatis.cache.impl.PerpetualCache 类的HashMap本地缓存,其作用域是SqlSession。在同一个SqlSession中两次执行相同的sql查询语句,第一次执行完毕后,会将查询结果写入到缓存中,第二次会从缓存中直接获取数据,而不再到数据库中进行查询,从而提高查询效率。
  • 当一个SqlSession结束后,该SqlSession中的一级缓存也就不存在了。myBatis默认一级缓存是开启状态,且不能关闭。

证明从一级缓存中读取数据的依据

// 证明从一级缓存中读取数据的依据
	@Test
	public void testSelectStudentByName2() {
		Student student = dao.selectStudentById(2);
		System.out.println(student);
		
		Student student2 = dao.selectStudentById2(2);
		System.out.println(student2);
	}
[DEBUG] ==>  Preparing: select tid id,tname name,tage age,score from student where tid=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
[DEBUG] ==>  Preparing: select tid id,tname name,tage age,score from student where tid=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
  • 缓存的底层实现是一个Map,Map的value是查询结果
  • Map的key,即查询依据,使用的ORM框架不同,查询依据是不同的。
  • 一级缓存中读取数据的依据
    • MyBatis的查询依据是:Hash值 + Sql的id + SQL语句
    • Hibernate的查询依据是:查询结果对象的id

增删改操作对一级缓存的影响

@Test
	public void testSelectInsertStudentByName() {
		Student student = dao.selectStudentById(2);
		System.out.println(student);
		
		// 增删改操作都会情况一级缓存
		dao.insertStudent(new Student("赵六",26,96.5));
		
		Student student2 = dao.selectStudentById(2);
		System.out.println(student2);
	}
[DEBUG] ==>  Preparing: select id,name,age,score from student where id=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
[DEBUG] ==>  Preparing: insert into student(name,age,score) values(?,?,?) 
[DEBUG] ==> Parameters: 赵六(String), 26(Integer), 96.5(Double)
[DEBUG] <==    Updates: 1
[DEBUG] ==>  Preparing: select id,name,age,score from student where id=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
  • 增删改操作都会情况一级缓存,无论是否提交

2. 二级缓存

  • 由于MyBatis从缓存中读取数据的依据与SQL的id相关,而非查询出的对象。所以,使用二级缓存的目的,不是在多个查询间共享查询结果(所有查询中只要查询结果中存在该对象的,就直接从缓存中读取,这是对查询结果的共享,但MyBatis不是),而是为了防止同一查询(相同的Sql id相同的sql语句的反复执行
  • myBatis内置的二级缓存为 org.apache.ibatis.cache.impl.PerpetualCache.

二级缓存的开启

  • 第一步
	<cache/>
	<insert id="insertStudent">
		insert into student(name,age,score) values(#{name},#{age},#{score})
	</insert>
	
	<select id="selectStudentById" resultType="Student">
		select id,name,age,score from student where id=#{xxx}
	</select>
	
	<select id="selectStudentById2" resultType="Student">
		select id,name,age,score from student where id=#{xxx}
	</select>
  • 第二步:实例化
    • 如果不实例化,程序会报java.io.NotSerializableException异常
public class Student implements Serializable {
  • 证明从二级缓存是存在的
	// 证明从二级缓存是存在的
	@Test
	public void testSelectStudentByName() {
		sqlSession = MyBatisUtils.getSqlSession();
		dao = sqlSession.getMapper(IStudentDao.class);
		Student student = dao.selectStudentById(2);
		System.out.println(student);
		
		sqlSession.close();
		
		sqlSession = MyBatisUtils.getSqlSession();
		dao = sqlSession.getMapper(IStudentDao.class);
		Student student2 = dao.selectStudentById(2);
		System.out.println(student2);
	}
[DEBUG] Cache Hit Ratio [com.huahua.dao.IStudentDao]: 0.0
[DEBUG] ==>  Preparing: select id,name,age,score from student where id=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
[DEBUG] Cache Hit Ratio [com.huahua.dao.IStudentDao]: 0.5
Student [id=2, name=王五, age=23, score=93.5]
  • 其中,Cache Hit Ratio:表示缓存命中率。第一次缓存命中率为0,这是因为程序第一次调用selectStudentById方法时,先从缓存空间中找结果,第一次因为缓存空间为空,所有没找到,一共找了1次结果为0;第二次缓存命中率为0.5,这是因为程序第二次调用selectStudentById方法时,先从缓存空间中找到了第一次调用方法时的缓存,找到了,此时,一共找了两次,找到了一次,所以结果为0.5;以此类推。
  • 就执行了一次select语句,证明二级缓存存在!
  • 开启内置的二级缓存步骤:
    • 1) 对实体进行序列化
    • 2)在映射文件中添加cache标签

增删改对二级缓存的影响

// 增删改对二级缓存的影响
	@Test
	public void testSelectInsertStudentByName() {
		sqlSession = MyBatisUtils.getSqlSession();
		dao = sqlSession.getMapper(IStudentDao.class);
		Student student = dao.selectStudentById(2);
		System.out.println(student);
		
		sqlSession.close();
		
		sqlSession = MyBatisUtils.getSqlSession();
		dao = sqlSession.getMapper(IStudentDao.class);
		
		// 插入
		dao.insertStudent(new Student("",0,0));
		
		Student student2 = dao.selectStudentById(2);
		System.out.println(student2);
	}
[DEBUG] Cache Hit Ratio [com.huahua.dao.IStudentDao]: 0.0
[DEBUG] ==>  Preparing: select id,name,age,score from student where id=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
[DEBUG] ==>  Preparing: insert into student(name,age,score) values(?,?,?) 
[DEBUG] ==> Parameters: (String), 0(Integer), 0.0(Double)
[DEBUG] <==    Updates: 1
[DEBUG] Cache Hit Ratio [com.huahua.dao.IStudentDao]: 0.5
[DEBUG] ==>  Preparing: select id,name,age,score from student where id=? 
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <==    Columns: id, name, age, score
[TRACE] <==        Row: 2, 王五, 23, 93.5
[DEBUG] <==      Total: 1
Student [id=2, name=王五, age=23, score=93.5]
  • 说明:
    • 1) 增删改同样也会清空二级缓存;
    • 2) 对于二级缓存的清空,实质上是对所查找的key对应的value置为null,而并非将<key, value>键值对,即Entry对象删除;
    • 3) 从DB中进行select查询的条件是:
      • ① 缓存中根本就不存在这个key所对应的Entry对象——根本就不存在这个key;
      • ② 缓存中存在该key所对应的Entry对象,但其value为null。

二级缓存的配置

为cache标签添加一些相关属性设置,可以对二级缓存的运行性能进行控制。当然,若不指定设置,则均保持默认值。
<cache eviction="FIFO" flushInterval="10800000" 
	   readOnly="true" size="512"/>
  • eviction:逐出策略。当二级缓存中的对象达到最大值时,就需要通过逐出策略将缓存中的对象移出缓存。默认为LRU。常用的策略有:
    • FIFO:First In First Out,先进先出
    • LRU:Least Recently Used,未被使用时间最长的
  • flushInterval:刷新缓存的时间间隔,单位毫秒。这里的刷新缓存即清空缓存。一般不指定,即当执行增删改时刷新缓存。
  • readOnly:设置缓存中数据是否只读。只读的缓存会给所有调用者返回缓存对象的相同实例,因此,这些对象不能修改,这提供了很重要的性能优势。但读写的缓存会返回缓存对象的拷贝。这会慢一些,但是安全,因此默认是false。
  • size:二级缓存中可以存放的最多对象个数。默认为1024个。

二级缓存的关闭

二级缓存默认开启状态。若要将其关闭,则需要进行相关设置。根据关闭的范围大小,可以分为全局关闭于局部关闭。
  • 全局关闭
    • 所谓全局关闭是指,整个应用的二级缓存全部关闭,所有查询均不使用二级缓存。全局开关设置在主配置文件的全局配置settings中,该属性为cacheEnabled,设置为false,则关闭;设置为true,则开启,默认值为true。即二级缓存默认是开启的。
	<settings>
		<!-- 关闭(全局)二级缓存 -->
		<setting name="cacheEnabled" value="false"/>
	</settings>
  • 局部关闭
	<select id="selectStudentById" useCache="false" resultType="Student">
		select id,name,age,score from student where id=#{xxx}
	</select>
	
	<select id="selectStudentById2" resultType="Student">
		select id,name,age,score from student where id=#{xxx}
	</select>

ehcache 二级缓存

  • 使用ehecache 二级缓存,实体类无需实现序列化接口。
  • 下载ehcache jar包
    在这里插入图片描述-
    • 第一步:导包
    • 第二步:
<cache type="org.mybatis.caches.ehcache.EncacheCache"/>
    • 第三步:需要ehcache的配置文件——ehcache-failsafe.xml(其在ehcache-core-2.6.8 jar包中),但不能直接用,需要改名,比如改成:ehcache.xml。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值