1、前言
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
看看我们常说说的缓存
这个就是我们常说的缓存,那么我们今天要看的肯定是mybatis缓存。
mybatis的缓存如下:
2、mybatis的一级缓存:
是sqlsession级别的缓存。就是你查询之后只要不commit(一般增删改才需要),就是不关闭sqlsession会话,就拥有缓存,mybatis默认支持一级缓存。
注意:如果与spring整合,我们还清楚的记得spring最后都要关闭sqlsession的,所以呀就不支持一级缓存了(虽然然并卵,但是还是值得我们学习一下)。
第一次发出一个查询sql,sql查询结果写入sqlsession的一级缓存中,缓存使用的数据结构是一个map<key,value>
key:hashcode+sql+sql输入参数+输出参数(sql的唯一标识)
value:用户信息
同一个sqlsession再次发出相同的sql,就从缓存中取不走数据库。如果两次中间出现commit操作(修改、添加、删除),本sqlsession中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。
key里面部分数据:100881661:3415680886:com.ycy.mybatis.dao.UserMapper.getUserById:0:2147483647:SELECT * FROM USER WHERE id=?:1:development
我们一直进入源码里面,进入execute里面去看看,因为我用的idea,进入源码看起来都吐了,没有Eclipse方便。没事可以看看,第二次他就读取这个key,那么久可以直接从原来的本地缓存读取。然后就这么完美的实现一级缓存。
实例:
package com.ycy.mybatis.test; import com.ycy.mybatis.dao.OrdersCustomMapper; import com.ycy.mybatis.dao.UserMapper; import com.ycy.mybatis.dao.impl.UserMappermpl; import com.ycy.mybatis.module.Orders; import com.ycy.mybatis.module.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * Created by Administrator on 2015/8/31 0031. */ public class MybatisTest9 { private SqlSessionFactory sqlSessionFactory = null; @Before public void before() throws IOException { String resource="SqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); sqlSessionFactory= new SqlSessionFactoryBuilder().build(in); } //一级缓存测试 @Test public void findOrderAndDetail() throws Exception { SqlSession sqlSession=sqlSessionFactory.openSession(); //第一次查询 UserMapper userMapper=sqlSession.getMapper(UserMapper.class); User user= userMapper.getUserById(1); System.out.println(user.getUsername()); //第二次查询(没有关闭sqlsession) User user2= userMapper.getUserById(1); System.out.println(user2.getUsername()); } }
3、mybatis二级缓存:
是mapper级别缓存,垮sqlsession的。同一个namespace命名空间的缓存,以命名空间为单位来创建缓存结构。
如果从二级缓存没有取到,再从一级缓存中找,如果一级缓存也没有,从数据库查询。
是map<key、value>。
配置二级缓存:
3.1、打开总开关
二级缓存 |
描述 |
允许值 |
默认值 |
cacheEnabled |
对在此配置文件下的所有cache 进行全局性开/关设置。 |
true false |
true |
<!-- 二级缓存总开关 --> <setting name="cacheEnabled" value="true"/>
3.2、mapper里面添加cache
要在你的Mapper映射文件中添加一行: <cache /> ,表示此mapper开启二级缓存。在mapper下面增加
<!--打开mapper二级缓存开关--> <cache/>
3.3、pojo中java对象需要实现序列化接口
public class User implements Serializable
<span style="font-size:12px;">package com.ycy.mybatis.test; import com.ycy.mybatis.dao.OrdersCustomMapper; import com.ycy.mybatis.dao.UserMapper; import com.ycy.mybatis.dao.impl.UserMappermpl; import com.ycy.mybatis.module.Orders; import com.ycy.mybatis.module.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * Created by Administrator on 2015/8/31 0031. */ public class MybatisTest9 { private SqlSessionFactory sqlSessionFactory = null; @Before public void before() throws IOException { String resource="SqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); sqlSessionFactory= new SqlSessionFactoryBuilder().build(in); } //一级缓存测试 @Test public void findOrderAndDetail() throws Exception { SqlSession sqlSession=sqlSessionFactory.openSession(); //第一次查询 UserMapper userMapper=sqlSession.getMapper(UserMapper.class); User user= userMapper.getUserById(1); System.out.println(user.getUsername()); //第二次查询(没有关闭sqlsession) User user2= userMapper.getUserById(1); System.out.println(user2.getUsername()); } //二级缓存测试 @Test public void cache2() throws Exception { SqlSession sqlSession=sqlSessionFactory.openSession(); SqlSession sqlSession2=sqlSessionFactory.openSession(); UserMapper userMapper=sqlSession.getMapper(UserMapper.class); UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class); //第一次查询 User user= userMapper.getUserById(1); System.out.println(user.getUsername()); sqlSession.close(); //第二次查询() User user2= userMapper2.getUserById(1); System.out.println(user2.getUsername()); sqlSession2.close(); } }</span>
我们主要看:这个表示缓存变为2次用了1次,0.5证明我们的缓存成功了哦
输出结果:
Cache Hit Ratio [com.ycy.mybatis.dao.UserMapper]: 0.5
17:35:47.516 [main] DEBUG com.ycy.mybatis.dao.UserMapper - Cache Hit Ratio [com.ycy.mybatis.dao.UserMapper]: 0.0 17:35:47.536 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection 17:35:47.912 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1674518905. 17:35:47.913 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@63cf2179] 17:35:47.917 [main] DEBUG com.ycy.mybatis.dao.UserMapper.getUserById - ==> Preparing: SELECT * FROM USER WHERE id=? 17:35:47.998 [main] DEBUG com.ycy.mybatis.dao.UserMapper.getUserById - ==> Parameters: 1(Integer) 17:35:48.024 [main] DEBUG com.ycy.mybatis.dao.UserMapper.getUserById - <== Total: 1 王五 17:35:48.031 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@63cf2179] 17:35:48.032 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@63cf2179] 17:35:48.032 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1674518905 to pool. 17:35:48.033 [main] DEBUG com.ycy.mybatis.dao.UserMapper - Cache Hit Ratio [com.ycy.mybatis.dao.UserMapper]: 0.5 王五
3.5、二级缓存关闭
对于变化频率较高的sql,需要禁用二级缓存:(因为变得我们记住它没得意思)
在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
<select id="getUserById" parameterType="int" resultType="User" useCache="false"> SELECT * FROM USER WHERE id=#{id} </select>不测试了哦,自己测试
如果你关闭之后去测试没有下面一行:
Cache Hit Ratio [com.ycy.mybatis.dao.UserMapper]: 0.0
3.6、刷新二级缓存
如果sqlsession操作commit操作,对二级缓存进行刷新(全局清空)。
设置statement的flushCache是否刷新缓存,默认值是true。
<!--新增用户--> <insert id="insertUser" parameterType="User" flushCache="true"> <!--插入值之后返回主键值--> <selectKey resultType="int" order="AFTER" keyProperty="id"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO USER (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) </insert>
你刷新之后,缓存依旧为0.0:
Cache Hit Ratio [com.ycy.mybatis.dao.UserMapper]: 0.0
3.7、 mybatis的cache参数(了解)
mybatis的cache参数只适用于mybatis维护缓存。
flushInterval:(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
如下例子:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:
1. LRU – 最近最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。