本文会介绍mybatis缓存的工作原理,以及mybatis缓存导致的脏数据问题,以及如何避免脏数据的问题;
一级缓存:
基础知识:
1、在一个Session中,在Session级别的一级缓存下,mybatis会将读取到的数据放入一级缓存中,Session内所有的增删改操作都会使得该session的一级缓存失效;
2、在Session级别的一级缓存下,mybatis查询时会先从一级缓存中查询,查得到数据则直接返回,查不到再去db查,然后把从db查到的数据放入一级缓存中;
3、会被Executor持有,有两个级别,一个是session级别,即缓存内容在一个session范围内共享;一个是statement级别,即缓存内容只在一个statement内共享;
4、在Statement级别的一级缓存中,在查询方法的最后,都会判断到当前一级缓存的级别是Statement,然后清空缓存;
时序图:
一级缓存导致的脏数据问题:
假如会话A读取了一条数据并缓存在自己的一级缓存中,会话B修改了该条数据;由以上时序图可知,mybatis都是先去一级缓存查,如果查到了直接返回,查不到才去查数据库。这种情况下若会话A再一次读取该条数据,则会直接从自己的一级缓存中读取,而无法读取到最新的数据;(要区分清楚,这和mysql的可重复读隔离特性没关系,因为会话A未开启事务也会读到脏数据);
如何避免一级缓存的脏数据问题:
别用Session级别的一级缓存,用Statement级别的;
二级缓存:
基础知识:
1、 二级缓存的生命周期范围是namespace(即一个xml文件),可被多个Session共享
2、 一个xml可以引用别的namespace的Cache,这样它俩就共享同一个二级缓存;可通过在xml中进行如下配置来引用<cache-ref namespace="mapper.xxxMapper"/>
3、 会话必须提交事务了mybatis才能把读到的数据放入二级缓存中;
4、 增删改操作会使得其namespace的二级缓存失效;
5、 打开二级缓存后,mybatis查询的顺序为:二级缓存->一级缓存->db
工作流程图:
二级缓存导致的脏数据问题:
若在某个namespace A进行多表查询时,查到的数据会放入A的二级缓存中。若还有别namespace B更新了刚才A的多表查询中某个表,那么A是无法感知这个更新的,若再次进行同样的多表查询,将直接从二级缓存中查出来,而不是重新去db查出新数据;
如何避免二级缓存的脏数据问题:
关闭二级缓存;
参考:https://tech.meituan.com/2018/01/19/mybatis-cache.html