5、MyBatis 中的一级和二级缓存

简介: 5、MyBatis 中的一级和二级缓存

1. 前言

1.1 什么是缓存

缓存是存在内存中的临时数据,通过将我们 经常查询但不常变的数据 放在内存中,当我们查询数据时就不在需要从磁盘读取,而只需要从缓存中查询即可,大大提升了查询的效率,解决了高并发系统的性能问题。


1.2 为什么需要缓存

既然我们可以直接从数据库中查询数据,那为什么还要需要缓存呢?通过使用缓存,我们能够减少和数据库之间的交互频率,减少系统开销,从而提高系统的效率。


2. MyBatis 缓存

MyBatis 内置了一个强大的事务性查询缓存机制,通过它能够十分方便的配置和定制。默认情况下,MyBatis 默认定义了两级缓存,而且为了提高扩展性,定义了缓存接口 Cache,我们能十分方便的实现 Cache 接口来自定义二级缓存。


一级缓存:也叫 本地缓存,默认情况下开启的缓存(SqlSession 级别的缓存);

二级缓存:基于 namespace 级别的缓存,需要我们手动进行开启和配置;

3. 一级缓存

也叫 本地缓存,在与数据库同一次会话期间查询到的数据放在本地缓存,当要再次获取相同数据时,直接从缓存获取即可,不用再次和数据库交互。


3.1 一级缓存原理


image.png每个 SqlSession 中都有一个 Executor,每个 Executor 中又有一个 LocalCache,当我们进行查询操作时,MyBatis 根据当前执行的语句生成 MapperdStatement,然后在 Local Cache 中进行查询,如果存在(命中),直接返回给用户。若缓存中不存在(未命中),则和数据库交互查询数据,将结果写入 Local Cache,同时返回给用户。


3.2 如何使用一级缓存

一级缓存即 SqlSession 级别的缓存,和我们之前的 CURD 操作差不多;


首先在接口中添加方法;

@Select("select * from user where id=#{id}")

User queryUserById(@Param("id") int id);

1

2

测试


@Test
public void testQueryUserById() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession.close();
}
  1. 结果

image.png通过结果可以看出,由于是在一次会话期间内(SqlSession 级别),所以此时的 SQL 语句只查询了一次,当第二次获取相同结果时,直接从缓存中取结果即可,也就解释了为什么 user1 和 user2 指向的是同一个对象;


3.3 一级缓存失效的情况

一级缓存是默认一直开启的,我们是关闭不了的。但是有时候一级缓存会出现失效的情况,主要可能是如下几种原因导致;


每个 SqlSession 中缓存独立

当我们使用不同的 SqlSession 时,有多少个 SqlSession 就需要向数据库发起多少次查询请求。


@Test
public void testQueryUserById() {
    SqlSession sqlSession1 = MybatisUtil.getSqlSession();
    UserDao mapper1 = sqlSession1.getMapper(UserDao.class);
    SqlSession sqlSession2 = MybatisUtil.getSqlSession();
    UserDao mapper2 = sqlSession2.getMapper(UserDao.class);
    User user1 = mapper1.queryUserById(1);
    System.out.println(user1);
    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession1.close();
    sqlSession2.close();
}

image.png

  1. 当前缓存中不存在该数据时

当位于同一个 SqlSession,但查询条件不同时,也会导致缓存失效;

@Test
    public void testQueryUserById1() {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        User user = mapper.queryUserById(1);
        System.out.println(user);
        User user2 = mapper.queryUserById(2);
        System.out.println(user2);
        System.out.println(user == user2);
        sqlSession.close();
    }

image.png

  1. 其他 CURD 操作对当前数据造成影响

假如在同一 SqlSession 中,在两次查询之间进行了其他的增删改等操作,当第二次查询进行时,就会重新执行 SQL 语句,导致缓存失效;

@Test
public void testQueryUserById() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    mapper.updateUser(new User(2,"小玉","8349823"));
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    System.out.println(user == user2);
    sqlSession.close();
}

image.png

  1. 手动清除

SqlSession 相同时,如果我们手动清除了缓存,那么也会导致缓存失效的情况出现。

@Test
public void testQueryUserById1() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    // 手动清除缓存
    sqlSession.clearCache();
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    System.out.println(user == user2);
    sqlSession.close();
}

image.png

4. 二级缓存

也叫 全局缓存,基于 namespace 的缓存,一个 namespace 对应一个二级缓存。

4.1 二级缓存原理image.png一级缓存的最大共享范围是一个 SqlSession 内部,若多个 SqlSession 之间要共享缓存,则需要用二级缓存。二级缓存一旦开启,将会有多个 CachingExecutor 来装饰 Executor,进入一级缓存的查询流程之前,先在 CachingExecutor 中进行二级缓存的查询,如上图。此时数据的查询流程是:


二级缓存 -> 一级缓存 -> 数据库


4.2 如何使用二级缓存

要使用二级缓存,通常需要有如下步骤:


首先在 MyBatis 配置文件(一般是 mybatis-config.xml)中开启二级缓存;

<setting name="cacheEnabled" value="true"/>

1

然后到对应的 xxxMapper.xml 中配置二级缓存;

<cache/>

1

配置之后,xxxMapper.xml 文件中的 select 语句将会被缓存,而 insert、update、delete 则会刷新缓存。此外还可以设置自定义属性值来修改默认属性;


属性 说明

eviction 清除策略

flushInterval 刷新间隔,单位是 ms

size 引用数目,默认为 1024

readOnly 默认为 false

而清除策略也主要有如下 4 种:


清除策略 说明

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

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

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

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

测试


@Test
public void testGetUserByPassword() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    UserDao mapper2 = sqlSession.getMapper(UserDao.class);
    User user = mapper.getUserByPassword("1234567");
    System.out.println(user);
    User user2 = mapper2.getUserByPassword("1234567");
    System.out.println(user2);
    System.out.println(user==user2);
    sqlSession.close();
    sqlSession2.close();
}
  1. 结果

image.png根据结果可以看出,此时的二级缓存已经生效。若是未生效,则会和一级缓存中的结果一致,两者指向不同的对象,但此时两个引用指向同一对象,说明二级缓存成功。




目录
相关文章
|
2月前
|
存储 缓存 NoSQL
mybatisplus一二级缓存
MyBatis-Plus 继承并优化了 MyBatis 的一级与二级缓存机制。一级缓存默认开启,作用于 SqlSession,适用于单次会话内的重复查询;二级缓存需手动开启,跨 SqlSession 共享,适合提升多用户并发性能。支持集成 Redis 等外部存储,增强缓存能力。
|
4月前
|
缓存 Java 数据库连接
Mybatis一级缓存详解
Mybatis一级缓存为开发者提供跨数据库操作的一致性保证,有效减轻数据库负担,提高系统性能。在使用过程中,需要结合实际业务场景选择性地启用一级缓存,以充分发挥其优势。同时,开发者需注意其局限性,并做好事务和并发控制,以确保系统的稳定性和数据的一致性。
148 20
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
6月前
|
缓存 Java 数据库连接
Mybatis一级缓存、二级缓存详讲
本文介绍了MyBatis中的查询缓存机制,包括一级缓存和二级缓存。一级缓存基于同一个SqlSession对象,重复查询相同数据时可直接从缓存中获取,减少数据库访问。执行`commit`操作会清空SqlSession缓存。二级缓存作用于同一namespace下的Mapper对象,支持数据共享,需手动开启并实现序列化接口。二级缓存通过将数据存储到硬盘文件中实现持久化,为优化性能,通常在关闭Session时批量写入缓存。文章还说明了缓存的使用场景及注意事项。
205 7
Mybatis一级缓存、二级缓存详讲
|
7月前
|
缓存 Java 数据库连接
十、MyBatis的缓存
十、MyBatis的缓存
131 6
|
8月前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
162 6
|
10月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
9月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
355 4
|
9月前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
150 1
|
10月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
383 5