自学内容网 自学内容网

Mysql--实战篇--mybatis cache(一级缓存,二级缓存,子查询主键主查询全部,查询条件加索引,覆盖索引等)

MyBatis是一个流行的Java持久层框架,它简化了JDBC的使用,允许开发者通过XML或注解的方式定义SQL语句,并将结果映射到Java对象。为了提高查询性能,MyBatis提供了缓存机制,可以在一定程度上减少数据库的访问次数,从而提升应用的响应速度。

MyBatis缓存分为两种类型:

  • 一级缓存(SqlSession级别缓存)
  • 二级缓存(Mapper级别缓存)

1、一级缓存(SqlSession级别缓存)

一级缓存是默认开启的,它是基于SqlSession的缓存。也就是说,只要在一个SqlSession的生命周期内执行相同的查询语句,并且参数和SQL语句完全相同,MyBatis会从缓存中返回之前查询的结果,而不会再次访问数据库。

(1)、SqlSession

SqlSession是MyBatis框架中的核心接口之一,它代表了Mybatis与数据库的一次会话(Session)。通过SqlSession,你可以执行SQL语句、管理事务、获取映射器(Mapper)等。它是MyBatis与数据库交互的主要入口,类似于JDBC中的Connection对象,但提供了更高层次的抽象和更简洁的API。

SqlSession的作用:

  • 执行SQL语句:通过SqlSession,你可以执行查询、插入、更新和删除操作。
  • 管理事务:SqlSession提供了对事务的控制,允许你开始、提交或回滚事务。
  • 获取Mapper接口:SqlSession可以根据Mapper接口生成对应的代理对象,方便你通过接口调用SQL语句。
  • 缓存管理:SqlSession内部维护了一级缓存,可以在同一个会话中重复使用查询结果,减少对数据库的访问。
  • 关闭资源:SqlSession是一个需要手动关闭的资源,确保在使用完毕后调用close()方法释放数据库连接和其他资源。

SqlSession的生命周期:
SqlSession 的生命周期通常与一次数据库操作相关联。具体如下:
1、创建SqlSession:通过SqlSessionFactory创建一个新的SqlSession实例。
2、执行操作:使用SqlSession执行SQL语句、管理事务或获取Mapper接口。
3、提交或回滚事务:根据业务逻辑决定是否提交或回滚事务。
4、关闭SqlSession:操作完成后,必须调用close()方法关闭SqlSession,以释放数据库连接和其他资源。

简单理解:
当服务端通过Mybatis操作数据库时,会先创建一个SqlSession对象,记录本次和数据库的会话信息。本次会话过程中,会将所有第一次查询sql的结果记录到SqlSession的一级缓存中。之后如果有一模一样的查询sql,则直接取一级缓存的结果而不会再次请求数据库。当本次事务提交或者回滚后,SqlSession就会被清理掉。即便下次在有和之前请求一模一样的查询sql,也会创建新的SqlSession在继续工作。
注意:
多线程的请求,每个线程也也都是创建自己独立的SqlSession,不会彼此共享。

(2)、一级缓存的工作原理

  • 缓存范围:一级缓存的作用范围是单个SqlSession。每个SqlSession都有自己的缓存,不同的SqlSession之间不会共享缓存。
  • 缓存失效:当SqlSession执行了任何增删改操作(如insert、update、delete)时,一级缓存会自动清空,以确保后续查询能够获取最新的数据。
  • 事务影响:在同一个SqlSession中,如果开启了事务,那么在事务提交或回滚之前,一级缓存中的数据不会被清除。只有当事务结束时,缓存才会被清空。

(3)、示例说明

假设我们有一个UserMapper接口,用于查询用户信息:

public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = {id}")
    User getUserById(Long id);
}

解释:
在同一个SqlSession中,连续两次调用getUserById(1),MyBatis会在第一次查询时将结果存储到一级缓存中,第二次查询时直接从缓存中读取,而不会再次访问数据库。(如:通过@Transactional开启了事务的代码块中,调用两次getUserById(1)方法)

(4)、注意事项

  • 一级缓存的作用范围有限,仅限于单个SqlSession。如果你使用的是Spring的事务管理器(如@Transactional注解),那么SqlSession的生命周期通常与事务的生命周期一致。
  • 如果你手动关闭或提交SqlSession,一级缓存也会被清空。

2、二级缓存(Mapper级别缓存)

二级缓存是跨SqlSession的缓存,它可以作用于多个SqlSession之间的查询结果。
二级缓存的作用范围是整个应用程序中使用相同Mapper的所有SqlSession。通过启用二级缓存,可以减少对数据库的频繁访问,提升查询性能。

简单理解:
对于同一个Mapper接口,所有的查询请求不论是哪一个Sqlsession执行的查询操作,都会在第一次查询后将查询结果保存到二级缓存中。之后只要是相同的查询sql就会直接从二级缓存中取数据而不会在请求数据库了。
对于不同的Mapper接口,彼此的缓存是彼此隔离的。

(1)、工作原理

  • 缓存范围:二级缓存的作用范围是整个应用程序中使用相同Mapper的所有SqlSession。不同Mapper之间的缓存是隔离的,不会相互影响。
  • 缓存失效:当某个SqlSession执行了与该Mapper相关的增删改操作时,二级缓存会自动清空,以确保后续查询能够获取最新的数据。
  • 缓存配置:二级缓存需要显式开启,并且可以通过XML或注解进行配置。也可以为每个Mapper单独配置缓存策略,也可以全局配置缓存。

(2)、配置方式

1、XML 配置

在XML文件中,使用标签来启用二级缓存。你可以指定缓存的类型、大小、清除策略等。

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 启用二级缓存 -->
    <cache 
        eviction="LRU"  <!-- 清除策略:FIFOLRUSOFTWEAK -->
        flushInterval="60000"  <!-- 自动刷新间隔(毫秒),默认不自动刷新 -->
        size="512"  <!-- 缓存的最大条目数 -->
        readOnly="false"  <!-- 是否只读,默认 false -->
    />
    
    <select id="getUserById" resultType="User">
        SELECT * FROM users WHERE id = {id}
    </select>
</mapper>

参数说明:

  • eviction:清除策略,决定了当缓存达到最大容量时如何移除旧的数据。常见的清除策略有:
    • FIFO:先进先出,最早进入缓存的数据将被最先移除。
    • LRU:最近最少使用,最长时间未被使用的数据将被移除。
    • SOFT:软引用,依赖于JVM的垃圾回收机制,只有在内存不足时才会被回收。
    • WEAK:弱引用,只要GC发生,就会被回收。
  • flushInterval:自动刷新间隔(以毫秒为单位)。设置后,MyBatis会在指定的时间间隔后自动清空二级缓存。如果你希望二级缓存一直有效,可以不设置此参数(即默认不自动刷新)。
  • size:缓存的最大条目数。超过这个数量时,缓存会根据eviction策略移除旧的数据。
  • readOnly:是否只读。如果设置为true,则缓存中的数据被认为是只读的,不会因为数据的变化而自动失效。这可以提高性能,但需要注意的是,如果数据在数据库中发生了变化,缓存中的数据可能会变得不一致。因此,readOnly=true通常适用于那些不经常变化的只读数据。

上述示例仅包含一个查询语句(select),如果该xml中包含多个select,也都是一样生效的。

2、注解配置

你也可以使用 @CacheNamespace注解来启用二级缓存。这个注解可以放在Mapper接口上。
示例:

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.annotations.CacheNamespace;

@CacheNamespace(
    implementation = org.apache.ibatis.cache.decorators.FifoCache.class,  // 缓存实现类
    eviction = "FIFO",  // 清除策略
    flushInterval = 60000,  // 自动刷新间隔(毫秒)
    size = 512,  // 缓存的最大条目数
    readOnly = false // 是否只读
)
public interface UserMapper {
    @Select("SELECT  FROM users WHERE id = {id}")
    User getUserById(Long id);
}
3、全局配置

在application.properties文件中添加配置。
配置示例:

# 启用二级缓存
mybatis.configuration.cache-enabled=true
# 启用懒加载
mybatis.configuration.lazy-loading-enabled=true
# 禁用激进的懒加载
mybatis.configuration.aggressive-lazy-loading=false
# 指定Mapper XML文件的位置
mybatis.mapper-locations=classpath:mapper/.xml

说明:

  • cache-enabled:用于启用或禁用二级缓存。默认值为true,表示启用二级缓存。
  • lazy-loading-enabled和aggressive-lazy-loading:这两个配置项与懒加载有关,虽然它们不是直接控制二级缓存的,但会影响缓存的行为。如果你启用了懒加载,可能会导致二级缓存中的数据延迟加载,影响性能。
4、引入第三方系统

可以使用第三方缓存系统(如Redis或Ehcache)作为MyBatis的二级缓存。
这里导入Redis的依赖和配置Redis的连接就不说了。

使用示例:

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 使用 Redis 作为二级缓存 -->
    <cache type="org.mybatis.caches.redis.RedisCache"/>
    
    <select id="getUserById" resultType="User">
        SELECT  FROM users WHERE id = {id}
    </select>
</mapper>

或者也可以使用注解:

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.annotations.CacheNamespace;

@CacheNamespace(
    implementation = RedisCache.class, eviction = RedisCache.class)
public interface UserMapper {
    @Select("SELECT  FROM users WHERE id = {id}")
    User getUserById(Long id);
}

(3)、缓存清除二级缓存

  • 手动清除缓存:你可以通过clearCache()方法手动清除当前SqlSession的一级缓存和二级缓存。
    示例:
try (SqlSession session = sqlSession.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);

    // 查询用户
    User user = mapper.getUserById(1);
    System.out.println(user);

    // 手动清除缓存
    mapper.clearCache();

    // 再次查询用户,此时会重新访问数据库
    User user2 = mapper.getUserById(1);
    System.out.println(user2);
}
  • 自动清除缓存:当执行与该Mapper相关的增删改操作时,MyBatis会自动清除二级缓存,以确保后续查询能够获取最新的数据。

(4)、二级缓存注意事项

  • 缓存一致性问题:二级缓存的作用范围是跨SqlSession的,因此可能会导致缓存中的数据与数据库中的数据不一致。为了避免这种情况,建议在多写少读的场景下谨慎使用二级缓存,或者设置合理的缓存清除策略。
  • 只读缓存:如果你将readOnly设置为true,则缓存中的数据被认为是只读的,MyBatis不会尝试更新缓存中的数据。这可以提高性能,但需要注意数据的一致性问题。
  • 缓存粒度:二级缓存的作用范围是整个Mapper,因此所有使用该Mapper的查询都会共享同一个缓存。如果你希望更细粒度地控制缓存,可以考虑使用自定义缓存实现。

(5)、二级缓存总结

  • 二级缓存是跨多个SqlSession的缓存机制,适用于多个线程或请求之间的数据共享。
  • 每个线程请求相同ID时,如果二级缓存中已经存在相应的数据,MyBatis会优先从二级缓存中获取数据,而不是再次查询数据库。
  • 多个select语句会共享同一个二级缓存,但MyBatis会根据查询的参数和SQL语句的不同,生成不同的CacheKey,以确保不同的查询不会互相干扰。
  • 二级缓存适合用于只读数据,对于频繁更新的数据,使用二级缓存可能会导致数据不一致的问题。

3、Mybatis一级缓存和二级缓存总结

  • 一级缓存优先于二级缓存:MyBatis会首先检查一级缓存,如果一级缓存中存在数据,则直接返回缓存中的结果,而不会去检查二级缓存。
  • 一级缓存的作用范围:仅限于当前SqlSession,不同的SqlSession之间不会共享一级缓存。
  • 二级缓存的作用范围:跨越多个SqlSession,适用于多个线程或请求之间的数据共享。
  • 缓存的触发顺序:MyBatis会先检查一级缓存,再检查二级缓存,最后才执行SQL查询。

对比如下:
在这里插入图片描述

乘风破浪会有时,直挂云帆济沧海!!!


原文地址:https://blog.csdn.net/qq_34207422/article/details/145214583

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!