13.缓存
简介
1.什么是缓存
1.存在内存中的临时数据
2.将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,
从缓存中查询,从而提高查询效率,解决高并发系统性能问题
2.为什么使用缓存
减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据使用缓存
经常查询并且不经常改变的数据
mybatis缓存
1.mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提升查询效率
2.mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
2.1默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
2.2二级缓存需要手动开启和配置,他是基于namespace级别的缓存
2.3为了提高扩展性,mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存
一级缓存
一级缓存也叫本地缓存
1.与数据库同义词会话启脚查询到的数据会放到本地缓存中
2.以后想要获取相同的数据,直接从缓存中拿,没有必要去查询数据库
样例1:在同一个sqlsession中查询同一id用户
@org.junit.Test
public void testCatch(){
SqlSession sqlSession = MybatisTools.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println("1:"+user1);
User user2 = mapper.queryUserById(1);
System.out.println("2"+user2);
System.out.println(user1==user2);
sqlSession.close();
}
控制台输出:
Checking to see if class cn.com.wmd.dao.PetMapper matches criteria [is assignable to Object]
Checking to see if class cn.com.wmd.dao.UserMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1240232440.
--------------------------------------------------------->>发现sql语句只执行了一遍
==> Preparing: select * from public.user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 无法, 五天
<== Total: 1
1:User(id=1, name=无法, pwd=五天)
2:User(id=1, name=无法, pwd=五天)
true----------------------------------------------------->>两次查询出的对象完全一致!
结论:
在同一sqlsession中多次执行的同一sql语句会放入一级缓存中,下次会去缓存中获取
样例2:不同的sqlsession执行同一sql语句
@org.junit.Test
public void testCatch(){
SqlSession sqlSession1 = MybatisTools.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.queryUserById(1);
System.out.println("1:"+user1);
sqlSession1.close();
System.out.println("----------------------分割线---------------------");
重点:两个sqlsession执行同一sql语句
SqlSession sqlSession2 = MybatisTools.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2= mapper2.queryUserById(1);
System.out.println("2"+user2);
sqlSession2.close();
System.out.println(user1==user2);
}
控制台输出:
Checking to see if class cn.com.wmd.dao.PetMapper matches criteria [is assignable to Object]
Checking to see if class cn.com.wmd.dao.UserMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1240232440.
==> Preparing: select * from public.user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 无法, 五天
<== Total: 1
1:User(id=1, name=无法, pwd=五天)
Closing JDBC Connection [org.postgresql.jdbc.PgConnection@49ec71f8]
Returned connection 1240232440 to pool.
----------------------分割线---------------------
Opening JDBC Connection
Checked out connection 1240232440 from pool.
==> Preparing: select * from public.user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 无法, 五天
<== Total: 1
2User(id=1, name=无法, pwd=五天)
Closing JDBC Connection [org.postgresql.jdbc.PgConnection@49ec71f8]
Returned connection 1240232440 to pool.
false
结论:发现同一查询语句执行了两次,并且两次查询出的对象不是同一对象
样例3:同一sqlsession执行同一sql语句,但在期间执行更新操作!
@org.junit.Test
public void testCatch(){
SqlSession sqlSession = MybatisTools.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println("1:"+user1);
Map<String, Object> map=new HashMap<String, Object>();
map.put("id",1);
map.put("pwd","wmd");
重点:在两次执行sql中执行更新操作!
mapper.updatePwd(map);
User user2 = mapper.queryUserById(1);
System.out.println("2:"+user2);
System.out.println(user1==user2);
sqlSession.close();
}
输出:发现执行了三次sql:两次相同查询,一次更新,并且两次查询出的对象不同
==> Preparing: select * from public.user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 无法, 五天
<== Total: 1
1:User(id=1, name=无法, pwd=五天)
==> Preparing: update public."user" SET pwd=? WHERE id=?
==> Parameters: wmd(String), 1(Integer)
<== Updates: 1
==> Preparing: select * from public.user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 无法, wmd
<== Total: 1
2:User(id=1, name=无法, pwd=wmd)
false
结论:一级缓存默认存在,并且在同一sqlsession中有效,若该sqlsession关闭,下次执行会重新查询
更新/插入/删除操作会清空缓存,下次重新查询!
二级缓存
1.二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
2.基于namespace级别的缓存,一个名称空间(即一个dao层接口),对应一个二级缓存
3.工作机制:
1.一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存中
2.如果当前缓存关闭了,这个会话对应的一级缓存就会销毁;但是我们想要会话关闭了,一级缓存中的数据会被保存到二级缓存中
3.新的会话查询信息,就可以从二级缓存中获取内容
4.不同的mapper查询出的数据会被放到自己对应的缓存中
样例:
1.在mybaits的主配置文件中配置设置:
<settings>
<!--配置日志打开--->
<setting name="logImpl" value="STDOUT_LOGGING"/>
重点:配置全局缓存设置,虽然默认是true,但是还是显示配置下!
<!--设置全局缓存开关-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在mapper.xml中配置<cache/>标签,也可以自定义一些参数,参数内容意思参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
1.可以直接配置个:<cache/>
2.也可以配置各种参数:参数内容意思参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
3.mapper.xml中的sql语句
<select id="queryUserByIdByBq" resultType="user">
select * from public."user" where id=#{id}
</select>
4.测试
@org.junit.Test
public void testCatch2(){
SqlSession sqlSession1 = MybatisTools.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.queryUserByIdByBq(1);
System.out.println("1:"+user1);
sqlSession1.close();
重点:虽然此处关闭了sqlsesson,但是这两个sql只执行一次,并且查询出的对象相同
System.out.println("---------------------分割线---------------------");
SqlSession sqlSession2 = MybatisTools.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.queryUserByIdByBq(1);
System.out.println("2"+user2);
System.out.println(user1==user2);
sqlSession2.close();
}
注意点:
但如果是标签:
@Select("select * from public.user where id=#{id}")
User queryUserById(@Param("id") int id);
这种形式的,即使开了全局缓存,两次不同的sqlsession查询也会查询查询两次,貌似全局缓存对sql标签标注的方法不起作用!
缓存原理
缓存的执行顺序
当查询语句过来时
1.先看二级缓存中是否有该记录
2.再看一级缓存中是否有该记录
3.如果都没有则查询数据库
自定义缓存
...