1.1 缓存简介
查询 : 连接数据库,耗资源
一次查询的结果,给他暂存一个可以直接取到的地方 --> 内存:缓存
我们再次查询的相同数据的时候,直接走缓存,不走数据库了
什么是缓存?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存
减少和数据库的交互次数,减少系统开销,提高系统效率。
什么样的数据能使用缓存
经常查询并且不经常改变的数据。
1.2 Mybatis缓存
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存(一个mapper 一个接口)。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
1.2.1 一级缓存
一级缓存也叫本地缓存。与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库。默认是开启的。
测试
1 编写接口方法
Blog queryUserById(@Param("id") int id);
2 mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxxx.dao.BlogMapper">
<select id="queryUserById" resultType="com.xxxx.pojo.Blog">
select * from blog where id = #{
id}
</select>
</mapper>
3 测试
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
try(SqlSession session = MyBatisUtils.getSqlSesion();){
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.queryUserById(1);
System.out.println(blog);
System.out.println("===========================");
Blog blog2 = mapper.queryUserById(1);
System.out.println(blog2);
System.out.println(blog == blog2);
}
}
}
分析: sql语句只执行了一次,并且用的是同一个对象。
1.2.2 一级缓存失效的四种情况
1、sqlSession不同
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
try(SqlSession session1 = MyBatisUtils.getSqlSesion();
SqlSession session2 = MyBatisUtils.getSqlSesion();
){
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
Blog blog1 = mapper1.queryUserById(1);
System.out.println(blog1);
System.out.println("===========================");
BlogMapper mapper2 = session2.getMapper(BlogMapper.class);
Blog blog2 = mapper2.queryUserById(1);
System.out.println(blog2);
System.out.println(blog1 == blog2);
}
}
}
分析:每个sqlSession中的缓存相互独立
2、sqlSession相同,查询条件不同
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
try(SqlSession session1 = MyBatisUtils.getSqlSesion();){
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
Blog blog1 = mapper1.queryUserById(1);
System.out.println(blog1);
System.out.println("===========================");
Blog blog2 = mapper1.queryUserById(2);
System.out.println(blog2);
System.out.println(blog1 == blog2);
}
}
}
分析:当前缓存中,不存在这个数据
3、sqlSession相同,两次查询之间执行了增删改操作!
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
try(SqlSession session1 = MyBatisUtils.getSqlSesion();){
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
Blog blog1 = mapper1.queryUserById(1);
System.out.println(blog1);
System.out.println("===========================");
Map map = new HashMap();
map.put("id" , "3");
map.put("author" , "xinxue");
mapper1.updateUserById(map);
System.out.println("===========================");
Blog blog2 = mapper1.queryUserById(1);
System.out.println(blog2);
System.out.println(blog1 == blog2);
}
}
}
分析:因为增删改操作可能会对当前数据产生影响
4 、sqlSession相同,手动清除一级缓存
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
try(SqlSession session1 = MyBatisUtils.getSqlSesion();){
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
Blog blog1 = mapper1.queryUserById(1);
System.out.println(blog1);
System.out.println("===========================");
session1.clearCache();
Blog blog2 = mapper1.queryUserById(1);
System.out.println(blog2);
System.out.println(blog1 == blog2);
}
}
}
1.2.2 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存。
- 工作机制:
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
新的会话查询信息,就可以从二级缓存中获取内容
不同的mapper查出的数据会放在自己对应的缓存(map)中
使用步骤
1、开启全局缓存 【mybatis-config.xml】
<!-- 开启全局缓存机制 默认就是开启的-->
<setting name="cacheEnabled" value="true"/>
2、去每个mapper.xml中配置使用二级缓存
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxxx.dao.BlogMapper">
**<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>**
<select id="queryUserById" resultType="com.xxxx.pojo.Blog">
select * from blog where id = #{
id}
</select>
<update id="updateUserById" parameterType="map">
update blog set author = #{
author} where id = #{
id}
</update>
</mapper>
3、代码测试
所有的实体类先实现序列化接口
package com.xxxx.dao;
import com.xxxx.pojo.Blog;
import com.xxxx.utils.IDUtil;
import com.xxxx.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.*;
public class DaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
@Test
public void test(){
SqlSession session1 = MyBatisUtils.getSqlSesion();
SqlSession session2 = MyBatisUtils.getSqlSesion();
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
BlogMapper mapper2 = session2.getMapper(BlogMapper.class);
Blog blog = mapper1.queryUserById(1);
System.out.println(blog);
session1.close();
System.out.println("=========================");
Blog blog1 = mapper2.queryUserById(1);
System.out.println(blog1);
session2.close();
}
}
1 只要开启了二级缓存,我们在同一个Mapper【namespace】中的查询,可以在二级缓存中拿到数据
2 查出的数据都会被默认先放在一级缓存中
3 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
1.2.3 缓存原理
1. 用户先查二级缓存
2. 再查询一级缓存
3. 最后查询数据库