一级缓存
一级缓存也叫本地缓存,在MyBatis中,一级缓存是在会话(SqlSession)层面实现的,这就说明一级缓存作用范围只能在同一个 SqlSession 中,跨 SqlSession 是无效的。
默认开启一级缓存.
二级缓存
二级缓存是mapper级别的缓存,如果多个SqlSession公用一个mapper文件,那么大家的第一次查询后的数据都会放在二级缓存中.二级缓存范围更大.
< setting name=“cacheEnabled” value=“true”/> 可以开启.
原理背诵
一级缓存是根据localcache去实现的,在Executor任意实现类取查询的时候都会执行父类 BaseExecutor 的 query 方法,在该方法里,首先会根据cachekey去查询缓存,如果没有,才会执行queryFromDatabase 方法,该方法会将查询结果放到缓存当中.
CacheKey 集合了 SQL 语句、SQL 参数、调用的 Mapper 方法名、SQL 分页参数、Environment 对象信息。
二级缓存是需要开启事务后才生效的,其底层是根据CachingExecutor 对象来实现的,该执行器里有一个TransactionalCacheManager 用来管理缓存,但其实也是使用了HashMap进行的一个缓存.在事务未提交之前,并不会真正缓存数据,而是先存储到一个临时属性,等事务提交之后才会真正存储到二级缓存。这么做的目的就是防止脏读。因为假如你在一个事务中修改了数据,然后去查询,这时候直接缓存了,那么假如事务回滚了呢?所以这里会先临时存储一下。在提交的方法sqlSession.commit() 中,我们会调用TransactionalCacheManager的commit方法,该方法里调用缓存管理器TransactionalCache来管理缓存,这里面有最重要的两个方法: flushPendingEntries 和 reset 两个方法, flushPendingEntries 方法负责把所有暂存区的内容刷新到缓存中,而 reset 方法则负责把本地暂存区清空, 同时把 clearOnCommit 置为 false。
对比
- 一级缓存是每个SqlSession独有的,因此如果在多线程环境下的写操作,一定会产生脏读问题,就是读到了另一个线程未提交的数据.而二级缓存是所有的SqlSession都可以共享一个缓存区域,与事务结合,可以解决脏读问题.
- 如果两个缓存都开启,只有在SqlSession关闭的时候,才会将数据保存在二级缓存中,如果按读取顺序,先读取二级缓存再读取一级缓存.
- 本人觉得,无论是一级缓存和二级缓存都是为了缓解数据库压力而生的,但是默认的一级缓存会存在脏读问题,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,而二级缓存又循环以事务开启,相对会变得复杂,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。