精华内容
下载资源
问答
  • mybatis的缓存机制
    2018-07-31 14:52:23

    出处微信公众号:美团技术团队

    更多相关内容
  • mybatis缓存机制

    2021-01-28 20:32:05
    MyBatis 缓存详解缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。跟Hibernate 一样,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。缓存体系结构:MyBatis 跟...

    MyBatis 缓存详解

    缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。跟Hibernate 一样,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。

    缓存体系结构:

    f4faeaa777730adaf35ec2dba6434dbb.png

    MyBatis 跟缓存相关的类都在cache 包里面,其中有一个Cache 接口,只有一个默认的实现类 PerpetualCache,它是用HashMap 实现的。我们可以通过 以下类找到这个缓存的庐山真面目

    DefaultSqlSession

    ->  BaseExecutor

    ->  PerpetualCache localCache

    ->private Map cache = new HashMap();

    除此之外,还有很多的装饰器,通过这些装饰器可以额外实现很多的功能:回收策略、日志记录、定时刷新等等。但是无论怎么装饰,经过多少层装饰,最后使用的还是基本的实现类(默认PerpetualCache)。可以通过 CachingExecutor 类 Debug 去查看。

    8b3ba846782be8caee91890d4f1da5bb.png

    所有的缓存实现类总体上可分为三类:基本缓存、淘汰算法缓存、装饰器缓存。

    6d68d10856b659f94dbe3b89d15c059d.png

    一级缓存(本地缓存):

    一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置。首先我们必须去弄清楚一个问题,在MyBatis 执行的流程里面,涉及到这么多的对象,那么缓存PerpetualCache 应该放在哪个对象里面去维护?如果要在同一个会话里面共享一级缓存,这个对象肯定是在SqlSession 里面创建的,作为SqlSession 的一个属性。

    DefaultSqlSession 里面只有两个属性,Configuration 是全局的,所以缓存只可能放在Executor 里面维护——SimpleExecutor/ReuseExecutor/BatchExecutor 的父类BaseExecutor 的构造函数中持有了PerpetualCache。在同一个会话里面,多次执行相同的SQL 语句,会直接从内存取到缓存的结果,不会再发送SQL 到数据库。但是不同的会话里面,即使执行的SQL 一模一样(通过一个Mapper 的同一个方法的相同参数调用),也不能使用到一级缓存。

    每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。

    在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。

    为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。

    如下图所示,MyBatis会在一次会话的表示----一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。

    e89adca358e6789f467f76ce56f116f8.png

    一级缓存的生命周期有多长?

    MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

    如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;

    如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;

    SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;

    SqlSession 一级缓存的工作流程:

    对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果​

    判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;​

    如果命中,则直接将缓存结果返回;​

    如果没命中:去数据库中查询数据,得到查询结果;

    将key和查询到的结果分别作为key,value对存储到Cache中;

    将查询结果返回;

    接下来我们来验证一下,MyBatis 的一级缓存到底是不是只能在一个会话里面共享,以及跨会话(不同session)操作相同的数据会产生什么问题。判断是否命中缓存:如果再次发送SQL 到数据库执行,说明没有命中缓存;如果直接打印对象,说明是从内存缓存中取到了结果。

    1、在同一个session 中共享(不同session 不能共享)

    //同Session

    SqlSession session1 =sqlSessionFactory.openSession();

    BlogMapper mapper1= session1.getMapper(BlogMapper.class);

    System.out.println(mapper1.selectBlogById(1002));

    System.out.println(mapper1.selectBlogById(1002));

    执行以上sql我们可以看到控制台打印如下信息(需配置mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl),会发现我们两次的查询就发送了一次查询数据库的操作,这说明了缓存在发生作用:

    5c8b15ac191e84152b1a065248cb0e5b.png

    PS:一级缓存在BaseExecutor 的query()——queryFromDatabase()中存入。在queryFromDatabase()之前会get()。

    public Listquery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

    。。。。。。try{++this.queryStack;//从缓存中获取

    list= resultHandler == null ? (List)this.localCache.getObject(key) : null;if (list != null) {this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

    }else{//缓存中获取不到,查询数据库

    list= this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

    }

    。。。。。。}

    2.同一个会话中,update(包括delete)会导致一级缓存被清空

    //同Session

    SqlSession session1 =sqlSessionFactory.openSession();

    BlogMapper mapper1= session1.getMapper(BlogMapper.class);

    System.out.println(mapper1.selectBlogById(1002));

    Blog blog3= newBlog();

    blog3.setBid(1002);

    blog3.setName("mybatis缓存机制修改");

    mapper1.updateBlog(blog3);

    session1.commit();// 注意要提交事务,否则不会清除缓存

    System.out.println(mapper1.selectBlogById(1002));

    一级缓存是在BaseExecutor 中的update()方法中调用clearLocalCache()清空的(无条件),query 中会判断。

    public intupdate(MappedStatement ms, Object parameter) throws SQLException {

    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (this.closed) {throw new ExecutorException("Executor was closed.");

    }else{

    //清除本地缓存this.clearLocalCache();return this.doUpdate(ms, parameter);

    }

    }

    3.其他会话更新了数据,导致读取到脏数据(一级缓存不能跨会话共享)

    SqlSession session1 =sqlSessionFactory.openSession();

    BlogMapper mapper1= session1.getMapper(BlogMapper.class);

    SqlSession session2=sqlSessionFactory.openSession();

    BlogMapper mapper2= session2.getMapper(BlogMapper.class);

    System.out.println(mapper2.selectBlogById(1002));

    Blog blog3= newBlog();

    blog3.setBid(1002);

    blog3.setName("mybatis缓存机制1");

    mapper1.updateBlog(blog3);

    session1.commit();

    System.out.println(mapper2.selectBlogById(1002));

    一级缓存的不足:

    使用一级缓存的时候,因为缓存不能跨会话共享,不同的会话之间对于相同的数据可能有不一样的缓存。在有多个会话或者分布式环境下,会存在脏数据的问题。如果要解决这个问题,就要用到二级缓存。MyBatis 一级缓存(MyBaits 称其为 Local Cache)无法关闭,但是有两种级别可选:

    session 级别的缓存,在同一个 sqlSession 内,对同样的查询将不再查询数据库,直接从缓存中。

    statement 级别的缓存,避坑: 为了避免这个问题,可以将一级缓存的级别设为 statement 级别的,这样每次查询结束都会清掉一级缓存。

    二级缓存:

    二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存   —> 一级缓存 —> 数据库。

    作为一个作用范围更广的缓存,它肯定是在SqlSession 的外层,否则不可能被多个SqlSession 共享。而一级缓存是在SqlSession 内部的,所以第一个问题,肯定是工作在一级缓存之前,也就是只有取不到二级缓存的情况下才到一个会话中去取一级缓存。第二个问题,二级缓存放在哪个对象中维护呢? 要跨会话共享的话,SqlSession 本身和它里面的BaseExecutor 已经满足不了需求了,那我们应该在BaseExecutor 之外创建一个对象。

    实际上MyBatis 用了一个装饰器的类来维护,就是CachingExecutor。如果启用了二级缓存,MyBatis 在创建Executor 对象的时候会对Executor 进行装饰。CachingExecutor 对于查询请求,会判断二级缓存是否有缓存结果,如果有就直接返回,如果没有委派交给真正的查询器Executor 实现类,比如SimpleExecutor 来执行查询,再走到一级缓存的流程。最后会把结果缓存起来,并且返回给用户。

    d4c873dfff4bcca4a9063c5bcec579db.png

    开启二级缓存的方法

    第一步:配置 mybatis.configuration.cache-enabled=true,只要没有显式地设置cacheEnabled=false,都会用CachingExecutor 装饰基本的执行器。

    第二步:在Mapper.xml 中配置标签:

    基本上就是这样。这个简单语句的效果如下:

    映射语句文件中的所有 select 语句的结果将会被缓存。

    映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

    缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

    缓存不会定时进行刷新(也就是说,没有刷新间隔)。

    缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

    缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

    这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。可用的清除策略有:

    LRU – 最近最少使用:移除最长时间不被使用的对象。

    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

    SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。

    WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

    默认的清除策略是 LRU。

    flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

    size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

    readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

    注:二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

    Mapper.xml 配置了之后,select()会被缓存。update()、delete()、insert()会刷新缓存。:如果cacheEnabled=true,Mapper.xml 没有配置标签,还有二级缓存吗?(没有)还会出现CachingExecutor 包装对象吗?(会)

    只要cacheEnabled=true 基本执行器就会被装饰。有没有配置,决定了在启动的时候会不会创建这个mapper 的Cache 对象,只是最终会影响到CachingExecutorquery 方法里面的判断。如果某些查询方法对数据的实时性要求很高,不需要二级缓存,怎么办?我们可以在单个Statement ID 上显式关闭二级缓存(默认是true):

    二级缓存验证(验证二级缓存需要先开启二级缓存)

    1、事务不提交,二级缓存不存在

    System.out.println(mapper1.selectBlogById(1002));

    //事务不提交的情况下,二级缓存不会写入

    // session1.commit();

    System.out.println(mapper2.selectBlogById(1002));

    为什么事务不提交,二级缓存不生效?因为二级缓存使用TransactionalCacheManager(TCM)来管理,最后又调用了TransactionalCache 的getObject()、putObject 和commit()方法,TransactionalCache里面又持有了真正的Cache 对象,比如是经过层层装饰的PerpetualCache。在putObject 的时候,只是添加到了entriesToAddOnCommit 里面,只有它的commit()方法被调用的时候才会调用flushPendingEntries()真正写入缓存。它就是在DefaultSqlSession 调用commit()的时候被调用的。

    2、使用不同的session 和mapper,验证二级缓存可以跨session 存在取消以上commit()的注释

    3、在其他的session 中执行增删改操作,验证缓存会被刷新

    System.out.println(mapper1.selectBlogById(1002));//主键自增返回测试

    Blog blog3 = newBlog();

    blog3.setBid(1002);

    blog3.setName("mybatis缓存机制");

    mapper1.updateBlog(blog3);

    session1.commit();

    System.out.println(mapper2.selectBlogById(1002));

    为什么增删改操作会清空缓存?在CachingExecutor 的update()方法里面会调用flushCacheIfRequired(ms),isFlushCacheRequired 就是从标签里面渠道的flushCache 的值。而增删改操作的flushCache 属性默认为true。

    什么时候开启二级缓存?

    一级缓存默认是打开的,二级缓存需要配置才可以开启。那么我们必须思考一个问题,在什么情况下才有必要去开启二级缓存?

    因为所有的增删改都会刷新二级缓存,导致二级缓存失效,所以适合在查询为主的应用中使用,比如历史交易、历史订单的查询。否则缓存就失去了意义。

    如果多个namespace 中有针对于同一个表的操作,比如Blog 表,如果在一个namespace 中刷新了缓存,另一个namespace 中没有刷新,就会出现读到脏数据的情况。所以,推荐在一个Mapper 里面只操作单表的情况使用。

    如果要让多个namespace 共享一个二级缓存,应该怎么做?跨namespace 的缓存共享的问题,可以使用来解决:

    cache-ref 代表引用别的命名空间的Cache 配置,两个命名空间的操作使用的是同一个Cache。在关联的表比较少,或者按照业务可以对表进行分组的时候可以使用。

    注意:在这种情况下,多个Mapper 的操作都会引起缓存刷新,缓存的意义已经不大了.

    第三方缓存做二级缓存

    除了MyBatis 自带的二级缓存之外,我们也可以通过实现Cache 接口来自定义二级缓存。MyBatis 官方提供了一些第三方缓存集成方式,比如ehcache 和redis:https://github.com/mybatis/redis-cache ,这里就不过多介绍了。当然,我们也可以使用独立的缓存服务,不使用MyBatis 自带的二级缓存。

    自定义缓存:

    除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

    这个示例展示了如何使用一个自定义的缓存实现。type 属性指定的类必须实现 org.mybatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。 这个接口是 MyBatis 框架中许多复杂的接口之一,但是行为却非常简单。

    public interfaceCache {

    String getId();

    intgetSize();

    voidputObject(Object key, Object value);

    Object getObject(Object key);

    boolean hasKey(Object key);

    Object removeObject(Object key);

    voidclear();

    }

    为了对你的缓存进行配置,只需要简单地在你的缓存实现中添加公有的 JavaBean 属性,然后通过 cache 元素传递属性值,例如,下面的例子将在你的缓存实现上调用一个名为 setCacheFile(String file) 的方法:

    你可以使用所有简单类型作为 JavaBean 属性的类型,MyBatis 会进行转换。 你也可以使用占位符(如 ${cache.file}),以便替换成在配置文件属性中定义的值。从版本 3.4.2 开始,MyBatis 已经支持在所有属性设置完毕之后,调用一个初始化方法。 如果想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject 接口。

    public interfaceInitializingObject {

    voidinitialize() throws Exception;

    }

    请注意,缓存的配置和缓存实例会被绑定到 SQL 映射文件的命名空间中。 因此,同一命名空间中的所有语句和缓存将通过命名空间绑定在一起。 每条语句可以自定义与缓存交互的方式,或将它们完全排除于缓存之外,这可以通过在每条语句上使用两个简单属性来达成。 默认情况下,语句会这样来配置:

    鉴于这是默认行为,显然你永远不应该以这样的方式显式配置一条语句。但如果你想改变默认的行为,只需要设置 flushCache 和 useCache 属性。比如,某些情况下你可能希望特定 select 语句的结果排除于缓存之外,或希望一条 select 语句清空缓存。类似地,你可能希望某些 update 语句执行时不要刷新缓存。

    展开全文
  • Mybatis缓存机制

    2022-06-25 11:45:19
    Mybatis缓存机制的介绍以及相关的配置信息

    缓存机制简介

    1. Mybatis包含一个非常强大的查询缓存特性,可以方便的进行配置和定制,缓存可以提升查询效率
    2. Mybatis系统中默认定义了两级缓存
      分别是一级缓存和二级缓存
    3. 默认情况下 只有一级缓存(SqlSession级别的缓存也称为本地缓存)开启
    4. 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
    5. 为了提高扩展性。Mybatis定义了缓存接口Cache。我们可以通过Cacahe来定义二级缓存。

    一级缓存的使用

    1. List item一级缓存【Local Cache】本地缓存 作用域默认是SqlSession。当Session flush或则是close后,Session中的所有Cacahe将被清空。
    2. 本地缓存不能被关闭,但是可以用clearCache()来清空缓存,或者改变缓存的作用域。
    3. 在Mybatis3.1之后 可以配置本地缓存的作用域。在Mybatis的全局配置文件中配置。设置名:localCacheScope,MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。
    4. 一级缓存的工作机制
      同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
      Key:hashCode +查询的Sql +编写的sql查询语句+参数
      这里是重点

    一级缓存失效的几种情况

    1. 不同的SqlSession 对应不同的一级缓存 —>就是创建的sqlsession不同 即从SqlSessionFactory.oppenSession()拿出两个不同的对象
    2. 同一个SqlSession但是查询条件不同
    3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
    4. 同一个SqlSession两次查询期间手动清了缓存
    5. 同一个SqlSession两次查询期间提交了事务

    二级缓存的使用

    1. 二级缓存【second level cache】全局作用域缓存
    2. 二级缓存默认不开启 需要手动配置
    3. Mybatis提供二级缓存的接口以及实现,缓存要求POJO实现Serializable接口
    4. 二级缓存在SqlSession关闭或提交之后才会生效
    5. 二级缓存的使用步骤
      1. 全局配置文件中开启二级缓存
      2. 需要使用二级缓存的映射文件处理使用cache配置缓存
      3. pojo需要实现Serializable接口
      4. 二级缓存在sqlSession关闭或提交之后才会生效
      5. 关闭sqlSession或者提交sqlSession时,将数据缓存到二级缓存
      6. 二级缓存相关属性:
        1. eviction=“FIFO” 清除缓存策略
        2. flushInterval 刷新间隔 单位毫秒
        3. size 应用数目 正整数
        4. readOnly 只读true/false

    缓存的相关属性设置

    1. 全局setting的cacheEnabled:配置二级缓存的开关 一级缓存一直是打开的
    2. select 标签的useCache属性:这个属性是用来配置这个select是否使用二级缓存 默认为true 一级缓存是一直使用的
    3. 所有的增删改查标签中的flushCache属性:增删改查默认为flushCache=true。Sql执行后会同时清空一级和二级缓存,默认查询是flushCache=false.
    4. sqlSession.clearCache():只是用来清除一级缓存

    整合第三方缓存

    1. 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
    2. EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider
    展开全文
  • mybatis缓存机制详解

    千次阅读 热门讨论 2021-12-16 10:12:13
    mybatis提供了缓存机制减轻数据库压力,提高数据库性能。mybatis的缓存分为一级和二级两种: 一级缓存:SqlSession级别的缓存,缓存的数据只在SqlSession内有效 二级缓存:mapper级别的缓存,同一个namespace公用这...

    mybatis提供了缓存机制减轻数据库压力,提高数据库性能。mybatis的缓存分为一级和二级两种:

    • 一级缓存:SqlSession级别的缓存,缓存的数据只在SqlSession内有效
    • 二级缓存:mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的

    一级缓存:

    一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空。

    一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。

    一级缓存是基于SQLSession的缓存,一级缓存的内容不能跨SQLSession。由mybatis自动维护。不同的sqlsession之间的缓存区域是互相不影响的。

    具体流程:

    • 第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来
    • 第二次执行select会从缓存中查数据,如果select相同且传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

    注意事项:

    • mybatis默认是开启一级缓存,不需要配置
    • 如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前SqlSession缓存中的所有缓存数据,这样可以保证缓存中存的数据永远和数据库中一致,避免出现脏读
    • 一个SqlSession结束后那么它里面的一级缓存也就不存在了。
    • mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的。意思就是,SqlSession的HashMap存储缓存数据时,是使用[namespace:sql:参数]作为key,查询返回的语句作为value保存的。

    二级缓存:

    二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。作用域为 namespance 是指对该 namespance 对应的配置文件中所有的 select 操作结果都缓存,这样不同线程之间就可以共用二级缓存。

    二级缓存可以设置返回的缓存对象策略:

    • 当 readOnly="true"时,表示二级缓存返回给所有调用者同一个缓存对象实例,调用者可以 update 获取的缓存实例,但是这样可能会造成其他调用者出现数据不一致的情况(因为所有调用者调用的是同一个实例)。
    • 当 readOnly=“false”(默认)时,返回给调用者的是二级缓存总缓存对象的拷贝,即不同调用者获取的是缓存对象不同的实例,这样调用者对各自的缓存对象的修改不会影响到其他的调用者,即是安全的。

    二级缓存是mapper级别的缓存,也就是同一个namespace中的mappe.xml。当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域。

    二级缓存是基于映射文件的缓存(namespace),缓存范围比一级缓存更大,不同的SQLSession可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。

    二级缓存默认是没有开启的。需要在setting全局参数中配置开启二级缓存

    <settings>
        <setting name="cacheEnabled" value="true"/>    <!--默认是false:关闭二级缓存-->
    <settings>
    

    在mapper.xml中配置:

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

    这里配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而却返回的对象是只读的。

    • eviction:代表的是缓存回收策略,目前MyBatis提供以下策略:
      • LRU(Least Recently Used):最近最少使用的,最长时间不用的对象
      • FIFO(First In First Out):先进先出,按对象进入缓存的顺序来移除他们
      • SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
      • WEAK:弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
        移除最长时间不用的对形象
    • flushInterval:刷新间隔时间,单位为毫秒,这里配置的是60000秒刷新,如果你不配置它,那么当
      SQL被执行的时候才会去刷新缓存。
    • size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
    • readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有办法修改缓存,他的默认值是false,不允许我们修改。

    对于访问多的查询请求并且用户对查询结果实时性要求不高的情况下,可采用mybatis二级缓存,降低数据库访问量,提高访问速度。
    若想禁用当前select语句的二级缓存,添加useCache=“false”(默认情况下为true)就可以了:

    <select id="getUserById" parameterType="int" resultType="user" useCache="false">
    

    在实际开发中,针对每次查询都需要最新数据的sql,要设置为useCache=“false” ,禁用二级缓存

    具体流程:

    • 当一个sqlseesion执行了一次select后,关闭此session的时候,会将查询结果缓存到二级缓存
    • 当另一个sqlsession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能

    注意事项:

    • 如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读
    • mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSession的HashMap存储缓存数据时,是使用[namespace:sql:参数]作为key,查询返回的语句作为value保存的。

    二级缓存的局限性
    mybatis二级缓存对细粒度级别的数据的缓存实现不好,比如:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息。因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。
    解决此类问题需要在业务层根据需求对数据有针对性缓存。

    SpringBoot整合Mybatis可通过以下设置二级缓存:

    • #使全局的映射器启用或禁用缓存。
      mybatis.configuration.cache-enabled=true
    • #全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。
      mybatis.configuration.lazy-loading-enabled=true
    • #当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。
      mybatis.configuration.aggressive-lazy-loading=true
    • #是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true
      mybatis.configuration.multiple-result-sets-enabled=true
    • #是否可以使用列的别名 (取决于驱动的兼容性) default:true
      mybatis.configuration.use-column-label=true
    • #允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false
      mybatis.configuration.use-generated-keys=true
    • #指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射\u3000PARTIAL:部分 FULL:全部
      mybatis.configuration.auto-mapping-behavior=partial
    • #这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)
      mybatis.configuration.default-executor-type=simple
    • #使用驼峰命名法转换字段。
      mybatis.configuration.map-underscore-to-camel-case=true
    • #设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session
      mybatis.configuration.local-cache-scope=session
    • #设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型
      mybatis.configuration.jdbc-type-for-null=null
    • #如果数据为空的字段,则该字段省略不显示,可以通过添加配置文件,规定查询数据为空是则返回null。
      mybatis.configuration.call-setters-on-nulls=true

    比如,通过以下代码可以禁用MyBatis的二级缓存:

    mybatis:
      type-aliases-package: com.hc.domain
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false #禁用二级缓存
      mapper-locations: mappers/*.xml  
    
    展开全文
  • MyBatis缓存机制详解

    千次阅读 2020-07-04 18:49:19
    MyBatis缓存机制详解MyBatis的缓存机制介绍一级缓存一级缓存调用流程一级缓存原理解释一级缓存使用注意事项二级缓存 MyBatis的缓存机制介绍    Mybatis包含一个非常强大的的查询缓存机制,可以减少系统...
  • mybatis 包含了一个非常强大的查询缓存特性,它可以非常方便地配置和定制,共有两级缓存机制。 当程序通过mybatis发起查询命令,mybatis则会根据程序发送的命令首先去缓存中寻找,如果命中缓存,则直接将缓存中的数据...
  • 聊聊MyBatis缓存机制

    千次阅读 多人点赞 2018-04-18 13:24:16
    点击上方“程序员小灰”,选择“置顶公众号”有趣有内涵的文章第一时间送达!本文转载自公众号 美团技术点评前言MyBatis是常见的Java数据库访问层框架。在日常工作中,开...
  • mybatis 一级缓存是 SqlSession 级别的缓存,默认支持一级缓存,不需要在配置文件去配置。 mybaits 的二级缓存是 mapper 范围级别,除了在 SqlMapConfig.xml 设置二级缓存的总开关<settingname=‘cacheEnabled’ ...
  • Mybatis缓存机制案例

    2018-08-03 15:20:20
    通过log4j打印来查看mybatis缓存实现的机制, 原博客http://blog.csdn.net/zouxucong/article/details/68947052
  • 本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。 MyBatis将数据缓存设计成两级结构,分为一级缓存、二级缓存: ...
  • java互联网架构-Mybatis缓存机制.pdf
  • MyBatis缓存机制深度解剖[收集].pdf
  • MyBatis缓存机制详解

    千次阅读 2022-06-03 10:24:21
    MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制,缓存可以极大的提升查询效率。MyBatis中默认定义了两级缓存,分别是一级缓存和二级缓存。 (1) 默认情况下,只有一级缓存(SqlSession级别的...
  • Mybatis缓存机制

    千次阅读 2022-03-24 10:53:08
    Mybatis缓存机制mybatis的代理详解mybatis缓存机制缓存介绍一级缓存一级缓存介绍一级缓存测试二级缓存二级缓存原理二级缓存使用步骤二级缓存使用测试 mybatis的代理详解 Java中动态代理主要是JDK动态代理(接口...
  • 这里写自定义目录标题MyBatis的工作原理MyBatis的工作原理MyBatis的主要成员Mybatis加载机制Mybatis缓存机制一级缓存说明:具体应用二级缓存说明:禁用二级缓存刷新二级缓存应用场景局限性 MyBatis的工作原理 ...
  • MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 ...
  • 本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,588
精华内容 19,435
关键字:

mybatis的缓存机制

友情链接: driven595.rar