缓存机制

Mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
Mybatis提供一级缓存和二级缓存。
http://ocx5m3vc3.bkt.clouddn.com/mybatis_%E6%9F%A5%E8%AF%A2%E7%BC%93%E5%AD%98.png

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

一级缓存

工作原理

http://ocx5m3vc3.bkt.clouddn.com/mybatis_%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.png
第一次发起查询的时候,先在缓存中查询是否有相应数据,有就直接返回;如果没有则从数据库获取信息并存储在一级缓存中。

如果SqlSession执行commit操作(执行插入、更新、删除)时,清空SqlSession中的一级缓存。这是为了让缓存中存储的是最新的信息,避免脏读。

测试

Mybatis默认支持一级缓存。不需要配置。

1
2
3
4
5
6
7
8
9
10
11
12
  @Test
public void testCacha_1(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

User user = userMapper.findUserById(1);
System.out.println(user);
// user.setName("heh");
// userMapper.updateUserName(user);
User user1 = userMapper.findUserById(1);
System.out.println(user1);
}

http://ocx5m3vc3.bkt.clouddn.com/Mybatis%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98debug.png
去掉代码中的注释后,输出:
http://ocx5m3vc3.bkt.clouddn.com/Mybatis%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98%E6%9B%B4%E6%96%B0debug.png

只用一级缓存时读取出来的值可能不是最新的。
例如:SqlSession1查询,SqlSession2修改,SqlSession1再查询,此时会导致SqlSession1第一次查找的结果不是最新的。此时就需要二级缓存了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testCacha_1_more(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

User user = userMapper1.findUserById(1);
System.out.println(user);

User user1 = new User();
user1.setName("zan");
user1.setId(1);
userMapper2.updateUserName(user1);

User user2 = userMapper1.findUserById(1);
System.out.println(user2);

}

http://ocx5m3vc3.bkt.clouddn.com/Mybatis%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98%E6%9B%B4%E6%96%B0%E4%B8%8D%E5%8F%8A%E6%97%B6.png

二级缓存

二级缓存原理

http://ocx5m3vc3.bkt.clouddn.com/mybatis_%E4%BA%8C%E7%BA%A7%E7%BC%93%E5%AD%98%E5%8E%9F%E7%90%86%E5%9B%BE.png

开启Mybatis的二级缓存后。

SqlSession1去查询信息,查询到的信息将会存储到二级缓存中。
如果SqlSession3去执行相同mapper下SQL,执行commit提交;将会清空二级缓存。
SqlSession去查询信息,现在二级缓存中查找数据。如果存在就直接取出。

二级缓存的范围更大,多个SqlSession可以共享Mapper的二级缓存区域。

开启二级缓存

  • 在SqlMapConfig.xml中加入

    1
    <setting name="cacheEnabled" value="true"/>
  • 在UserMapper.xml中开启二级缓存。UserMapper.xml下的sql磁性完成会存储到它的缓存区域。

    1
    2
    3
    4
    <mapper namespace="dao.UserMapper">
    <cache />
    ...
    </mapper>
  • 因为二级缓存存储介质多种多样,不一定在内存。对应的实体需要实现序列化接口。

    1
    2
    3
    public class User implements Serializable{
    ....
    }

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testCacha_2(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

User user1 = userMapper1.findUserById(1);
System.out.println(user1);

// User user = new User();
// user.setId(2);
// user.setName("GG");
// userMapper1.updateUserName(user);

sqlSession1.close();


SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

User user2 = userMapper2.findUserById(1);
System.out.println(user2);

sqlSession2.close();
}

useCache配置

在statement中设置<font color = ‘red”>useCache=”false”可以禁用当前select的二级缓存,即每次查询都会发出SQL去查询。默认情况是true。
如果每次查询都需要最新的数据sql时要禁用二级缓存。

1
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

刷新缓存

刷新缓存就是清空缓存。在mapper的同一个namespace中,如果有其他insert、update、delete操作数据后需要刷新缓存,否则会出现脏读现象。

设置statement配置中的<font color = ‘red”>flushCache=”true”

1
<insert id="insertUser" parameterType="pojo.User" flushCache="true">

应用场景和局限性

  • 应用场景
    对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
    实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

  • 局限性
    mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。