mybatis一级缓存、二级缓存以及集成EnCache、Redis,避免脏读
mybatis一级缓存、二级缓存以及集成EnCache、Redis,避免脏读
参考书目:《mybatis从入门到精通》 刘增辉 著
作者GitHub:
一级缓存
mybatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,mybatis会把执行的方法和参数生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已存在该键值时,则会返回缓存中的对象。
因此,一般在对数据库进行更新操作后需要清空缓存,而查询操作之后一般不要清空缓存,否则会降低查询性能。
mybatis通过在Mapper.xml中接口的实现中添加属性flushCache="true"来实现清空一级缓存。
完整示例如下:
<select id ="selectById" flushCache="true" resultMap = "userMap">select * from user where id = #{id}
</select>
二级缓存
不同于一级缓存只存在于SqlSession生命周期中,而是存在于SqlSessionFactory的生命周期中。当存在多个SqlSessionFactory时,它们的缓存都是绑定在各自对象上的,缓存数据都是绑定在各自对象上,缓存数据一般情况下是不相通的。只有在使用如Redis这样的缓存数据库时,才可以共享缓存。
mybatis默认开启二级缓存,因此无需设置,如果要关闭二级缓存,可以在mybatis的配置文件中将
<setting name = "cacheEnabled" value="false"> 该值默认为true,也就是开启二级换窜。
如果使用可读写缓存,可以使用SerializedCache序列化缓存。这个缓存类要求所有被序列化的对象必须实现Serializable接口,所以需要将实体类修改为实现了该接口的类。
同样也可以使用EnCache和Redis实现二级缓存。
脏数据的产生和避免
二级缓存虽然能提高效率,减轻数据库服务器的压力,但是如果使用不当很容易产生脏数据。
mybatis的二级缓存是和命名空间绑定的,所以通常情况下一个Mapper映射文件都拥有自己的二级缓存,不同Mapper的二级缓存互不影响。关系型数据库常会用到多表联合查询。在关联多表查询时肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间下的二级缓存中。涉及这些表的增删改查操作通常不在一个映射文件中,因此在其他命名空间下的二级缓存发生改变时,多表查询的二级缓存可能并没有改变,就会发生脏读。
如何避免出现脏读?
需要借助参照缓存,当某几个表可以作为一个业务整体时,通常是让几个会关联的ER表同时使用同一个二级缓存,这样就能解决脏数据问题。
示例如下:
<mapper namespace="UserMapper"><cache-ref namespace="RoleMapper"/><!--其他配置-->
</mapper>
二级缓存适用场景:
- 以查询为主的应用中,只有尽可能少的增删改操作
- 绝大多数以表单操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
- 可以按业务划分对表进行分组时,如关联表比较少,可以通过参照缓存设置。
- 如果脏读对系统无影响,也可以考虑使用。
- 在无法保证数据不出现脏读的情况下,建议在业务层使用可控制的缓存代替二级缓存。