Mybatis-Mybatis缓存机制&Mybatis动态代理机制

Mybatis缓存机制:

我们一般都会使用mybatis的默认缓存配置,但是mybatis的缓存机制有不足之处,使用中可能会造成脏数据问题。

Mybatis一级缓存:

mybatis一级缓存是sqlsession级别的。一级缓存的作用域是一个sqlsession。mbatis默认开启一级缓存
在同一个sqlsession中,执行相同的查询sql。第一次会去查询数据库,并写道缓存中,第二次直接从缓存中读取。若对应数据发生增删改操作,则缓存失效。
Mybatis-Mybatis缓存机制&Mybatis动态代理机制_第1张图片
一级缓存是以sqlsession为单位进行划分的,若找不到在去数据库查询,然后将结果写到缓存中。mybatis内部使用HashMap ,key为hashcode+statementid+sql语句。value为查询出来的结果集映射成的java对象。

一级缓存带来的脏数据问题:

当使用两个或者两个以上的sqlsession时,由于缓存数据是sqlsession内共享的,当另一个sqlsession进行增删改操作时,其它sqlsession时感知不到,因此其此时缓存中的数据编程脏数据。

@Test
public void testLocalCacheScope() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 
 
        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
 
        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("小岑",1) + "个学生的数据");
        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
}

Mybatis-Mybatis缓存机制&Mybatis动态代理机制_第2张图片

一级缓存总结:
  1. mybatis一级缓存声明周期和sqlsession一致。
  2. mybatis缓存内部设计简单,只有一个没有容量限制的hashmap,缓存性能有欠缺/
  3. mybatis一级缓存的最大范围是sqlsession内部,在多个sqlsession或者分布式环境下,数据库写操作会引起脏数据问题

Mybatis二级缓存:

mybatis二级缓存指mapper映射文件。二级缓存的作用域是同一个namespace(命名空间)下的mapper映射文件内容。多个sqlsession共享。
Mybatis需要手动设置二级缓存
同一个namespace下的mapper文件中,执行相同的sql查询语句,第一次会读取数据库,并写道缓存中,若中间没有进行增删改操作,则从缓存读取查询结果。
Mybatis-Mybatis缓存机制&Mybatis动态代理机制_第3张图片

配置二级缓存:
  • 在全局配置文件中进行如下配置
<settings>
    
    <setting name="lazyLoadingEnabled" value="true"/>
    
    <setting name="aggressiveLazyLoading" value="false"/>
     
     <setting name="cacheEnabled" value="true"/>
settings>

  • 在对应mapper下进行如下配置:
  1. cache可配置参数
type:cache使用的类型,默认是PerpetualCache,这在一级缓存中提到过。
 
eviction: 定义回收的策略,常见的有FIFO,LRU。
 
flushInterval: 配置一定时间自动刷新缓存,单位是毫秒。
 
size: 最多缓存对象的个数。
 
readOnly: 是否只读,若配置可读写,则需要对应的实体类能够序列化。
 
blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
  1. 映射的java对象实现序列化

  2. 映射文件对应的需要开启二级缓存的namespace进行如下设置

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


<cache-ref namespace="mapper.StudentMapper"/>
 
 //一二级缓存均有用
select 标签下:user  userCache标签 是否使用缓存
update/selete/update 标签下  : flushCache标签  刷新缓存
注意: MyBatis的二级缓存不适应用于映射文件中存在多表查询的情况。

通常我们会为每个单表创建单独的映射文件,由于MyBatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行的修改,引发脏数据问题。
可以使用Cache-ref,这样两个映射文件对应的Sql操作都使用的是同一块缓存了。
不过这样做的后果是,缓存的粒度变粗了,多个Mapper namespace下的所有操作都会对缓存使用造成影响。

二级缓存总结:
  1. 二级缓存相对于一级缓存来说,实现了sqlsession间的缓存数据共享,粒度到namespace级别,
  2. mybatis在进行多表查询时,极大可能会出现脏数据,有设计上缺陷,安全使用二级缓存的条件比较苛刻。
  3. 在分布式环境下,由于默认的mybatis cache都是基于本地的,分布式环境下必然会出现脏数据问题(多个server服务器都会进行缓存,且互不通知数据更新),需要使用集中式缓存将mybatis的cache接口实现,有一定的开发成本。相比较下,直接使用redis等分布式缓存可能成本更低,安全性更高。
建议在生产环境下关闭mybatis缓存,只作为一个ORM(对象关系映射)框架

Mybatis动态代理机制:

参考博客:

美团技术聊聊MyBatis缓存机制

你可能感兴趣的:(Mybatis)