[mybatis] 缓存

 

一级缓存

一级缓存(也称为本地缓存)默认会启用,且不能被控制。

何时缓存

一级缓存如何起作用?

Mybatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。

如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已存在该键值时,则会返回缓存中的对象。

可以通过在<select>属性中设置flushCache="true",每次查询都会清空当前的一级缓存,每次都会重新从数据库中查询数据。

何时更新缓存

任何的INSERTUPDATEDELETE操作都会清空一级缓存。(尽管操作的是无关数据)

二级缓存

不同于一级缓存在SqlSession生命周期中,二级缓存存在于SqlSessionFactory的生命周期中,多个SqlSessionFactory时,缓存数据不相通。

Mybatis的全局配置中,有一个参数cacheEnabled是二级缓存的开关。(默认为true)

二级缓存是和命名空间绑定的,即二级缓存需要配置在Mapper.xml映射文件中,或者Mapper.java接口中。

如何配置二级缓存

使用时,需要在Mapper.xml中添加<cache />元素即可。或Mapper.java接口注解@CacheNamespace

默认的二级缓存效果:

  • 映射语句文件中的所有SELECT语句将会被缓存。
  • 映射语句文件中所有INSERTUPDATEDELETE语句会刷新缓存。
  • 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来回收。
  • 根据时间表(如:no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储集合/对象(无论查询方法返回什么类型的值)的1024个引用。
  • 缓存会被视为read/write(可读/可写)的,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。(线程安全)

可以修改配置:

<cache
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>

@CacheNamespace(
    eviction = FifoCache.class,
    flushInterval = 60000,
    size = 512,
    readWrite = true
)
public interface RoleMapper{

}

该配置创建了一个FIFO缓存,每个60秒刷新一次,存储集合或对象的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

xml和java接口配置不能同时使用,因为它们相同的命名空间。

如何使用缓存

配置了二级缓存后,二级缓存就开始其作用了。

当调用了close()方法关闭了SqlSession时,SqlSession才会保存查询数据到二级缓存中,在这之后二级缓存才有了缓存数据。

二级缓存适用场景

二级缓存虽然好处多,但并不是什么情况都可以用。

以下场景比较推荐:

  • 以查询为主的应用,只有尽可能少的增、删、改。
  • 绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
  • 可以按业务划分对表进行分组时,如关联的表比较少,可以通过参照缓存进行配置。

可读写与只读的缓存差别

配置了可读写的缓存时,Mybatis使用SerializedCache(org.apache.ibatis.cache.decorators.SerializedCache)序列化缓存来实现可读写缓存类,并通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个新的实例。

因为要使用序列化,这个被缓存的类要求实现Serializable接口。

如果配置为只读缓存,MyBatis就会使用Map来存储缓存值,从缓存中获取到的对象就是同一个实例。

readOnly提供能很重要的性能优势。

  • 只读的缓存只会给所有调用者返回缓存的相同实例。
  • 可读写的缓存会通过序列化返回缓存对象的拷贝,这种方式会慢一些,但安全。

集成Redis缓存

官方提供了redis-cache集成Redis数据库。

步骤:

  1. 添加依赖mybatis-redis
  2. 配置redis,src/main/resources添加redis.properties文件。
host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=
clientName=
  1. 修改Mapper.xml。
<mapper namespace="com.demo.mapper.RoleMapper">
    <cache type="org.mybatis.caches.redis.RedisCache" />
</mapper>

RedisCache在保存缓存数据和获取缓存数据时,使用java的序列化和反序列化,因此还需要保证被缓存的对象必须实现Serializable接口。