缓存
一级缓存:
默认开启,将查询的记录缓存在 SqlSession 中,同一个session 下次再查询时直接命中。
好处:减轻数据库压力
问题:会有脏数据 当session1 中查询过一次数据之后,session2 更新数据并提交事务,session1再次查询数据,数据还是原来的状态。重
更新策略:本session 内进行 update delete 操作 的时候清空缓存中的内容(即使更新的不是缓存中的对象也会清空)。
新开启一个session 查询到的就是 新的数据.
如果不想从缓存中获取数据可以 先清理缓存 再查询session.clearCache()
既然有脏数据的问题,为什么还默认开启一级缓存?因为很少会再一个session 中多次获取同一个对象。写程序的时候应该注意。
缓存验证
查询两次数据只执行了一次sql查询
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
User user= mapper.selectByPrimaryKey("1");
System.out.println(user.getName());
user= mapper.selectByPrimaryKey("1");
System.out.println(user.getName());
结果:
验证脏数据:
session=getSqlSession();
SqlSession session2=getSqlSession();
UserMapper mapper= session.getMapper(UserMapper.class);
UserMapper mapper2= session2.getMapper(UserMapper.class);
User user= mapper.selectByPrimaryKey("1");
System.out.println("session1第一次查询的值:"+user.getName());
user= mapper.selectByPrimaryKey("1");
System.out.println("session1查询到缓存的值:"+user.getName());
user= mapper2.selectByPrimaryKey("1");
System.out.println("session2查询到的值:"+user.getName());
session1执行完第一次查询后,用断点暂停,用另外一个sessiong更新数据并提交事务。
如下:
session=getSqlSession();
UserMapper mapper= session.getMapper(UserMapper.class);
User user= mapper.selectByPrimaryKey("1");
System.out.println(user.getName());
User u2= new User();u2.setId(user.getId());u2.setSex(user.getSex());
u2.setName("username23");
mapper.updateByPrimaryKey(u2);
session.commit();
更新完成后 继续执行程序1中 session1 的第二次查询和 session2 中第一次查询 结果如下:
二级缓存
默认关闭,不建议使用,一般用redis等第三方缓存来替代。
开启方式
settings 配置 cacheEnabled
同时Mapp.xml 文件中 增加 cache 标签 开启本 Mapper 的 二级缓存
同时Map.xml 文件中 的 select 标签 可以通过 useCache 设置是否缓存查询结果 默认为true
代码:
SqlSession session=getSqlSession();
SqlSession session2=getSqlSession();
UserMapper mapper= session.getMapper(UserMapper.class);
UserMapper mapper2= session2.getMapper(UserMapper.class);
User user= mapper.selectByPrimaryKey("1");
System.out.println("session1第一次查询的值:"+user.getName());
user= mapper.selectByPrimaryKey("1");
System.out.println("session1查询到缓存的值:"+user.getName());
session.close();
user= mapper2.selectByPrimaryKey("1");
System.out.println("session2查询到的值:"+user.getName());
session2.close();
执行结果 可以看到 只执行了一次sql语句,第二次查询是从 session 的一级缓存中获得的所以二级缓存命中为0 ,第三次查询从二级缓存中获得, 命中率为 0.3333,(只有当查询的session 关闭后,才会缓存到二级缓存,如果session 1在session2 查询前未关闭那么,session无法从缓存中命中):
18:45:28.670 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@351d0846]
18:45:28.673 [main] DEBUG com.test.mybatis.mapper.UserMapper.selectByPrimaryKey - ==> Preparing: select id, name, sex from user where id = ?
18:45:28.701 [main] DEBUG com.test.mybatis.mapper.UserMapper.selectByPrimaryKey - ==> Parameters: 1(String)
18:45:28.719 [main] DEBUG com.test.mybatis.mapper.UserMapper.selectByPrimaryKey - <== Total: 1
session1第一次查询的值:username23_fix
18:45:28.720 [main] DEBUG com.test.mybatis.mapper.UserMapper - Cache Hit Ratio [com.test.mybatis.mapper.UserMapper]: 0.0
session1查询到缓存的值:username23_fix
18:45:28.726 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@351d0846]
18:45:28.727 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@351d0846]
18:45:28.727 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 891095110 to pool.
18:45:28.728 [main] DEBUG com.test.mybatis.mapper.UserMapper - Cache Hit Ratio [com.test.mybatis.mapper.UserMapper]: 0.3333333333333333
session2查询到的值:username23_fix
二级缓存以Mapper 命名空间为单位,有脏数据的问题 和 缓存刷新粒度粗的问题:
脏数据问题:UserMapper 查询了 Teacher 表的数据 做了缓存。TeacherMapper 中对 teacher 表数据做了更新,然后刷新了 TeacherMapper 的缓存但是 UserMapper 表中的缓存没有清空,就有了脏数据。
**粒度粗的问题:**不管增删改 那一条数据,只要有增删改操作 那么 该Mapper 下的缓存全部清空。
分布式缓存
们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)
不使用分布缓存,缓存的数据在各各服务单独存储,会有重复缓存的情况浪费资源。所以要使用分布式缓存对缓存数据进行集中管理。mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。其他第三方只要实现Mybatis 的Cache 接口 ,并实现指定功能即可。这里和redis整合
事先准备好redis服务.
然后 Mybatis 工程 增加依赖:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
增加redis 配置文件 redis.properties内容如下(配置好redis 的地址端口和超时时间):
host=zk01
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0
clientName=
UserMapper.xml 中 配置缓存实现为org.mybatis.caches.redis.RedisCache 该类实现了Mybatis 的Cache接口
<cache type="org.mybatis.caches.redis.RedisCache" eviction="FIFO" flushInterval="6000" size="512" readOnly="true" />
然后执行UserMapper 的查询操作 redis 中多了缓存内容如下,可以看到缓存的键组成为:命名空间+方法名++sql+参数值+数据源: