MyBatis 是一个流行的 Java 持久层框架,它封装了 JDBC,使数据库交互变得更简单、直观。MyBatis 支持两级缓存:一级缓存(Local Cache)和二级缓存(Global Cache),通过这两级缓存可以有效地减少数据库的访问次数,提高应用性能。
一级缓存(Local Cache)
一级缓存是指 MyBatis 默认的缓存,其作用域为 SqlSession。每当一个 SqlSession 被创建时,一个新的一级缓存也会被创建。这意味着,所有在同一个 SqlSession 中执行的查询,都会使用这个缓存。如果同一个 SqlSession 中执行了两次相同的 SQL 查询,第一次执行后的查询结果会被放在一级缓存中,第二次查询就会直接从缓存中获取结果,不需要再去查询数据库。
一级缓存的生命周期与 SqlSession 的生命周期相同,当 SqlSession 被关闭时,其对应的一级缓存也就消失了。
需要注意的是,如果在两次相同查询之间执行了增删改操作(这些操作可能会改变数据),MyBatis 会清空缓存,以确保缓存中存储的是最新的数据。
二级缓存(Global Cache)
与一级缓存不同,二级缓存的作用域不是 SqlSession 而是 Mapper 的命名空间。这意味着,来自不同 SqlSession 的相同命名空间的相同查询可以共享缓存数据。
二级缓存需要进行显式的配置才能使用。在 MyBatis 配置文件中开启二级缓存,并在需要使用二级缓存的 Mapper 文件中添加相应配置。开启后,查询结果会被缓存,并且在多个 SqlSession 间共享。
二级缓存比一级缓存拥有更广的作用范围,但也因此涉及更多的管理和维护工作,比如处理缓存同步的问题。为确保数据的一致性,当有增删改操作时,MyBatis 会清空受影响的二级缓存区域。
SqlSession的作用域
在 MyBatis 中,SqlSession
的作用域是指 SqlSession
生命周期的管理范围,它对于确保数据一致性、管理事务和优化连接资源等方面非常关键。正确理解和使用 SqlSession
的作用域对于开发高效、稳定的 MyBatis 应用至关重要。具体来说,SqlSession
的作用域主要有两种类型:
1.局部作用域(Local Scope):
- 在这个作用域中,
SqlSession
应当被创建和关闭,其生命周期仅限于方法或请求范围内。 - 通常情况下,每当接收到一个 HTTP 请求或调用一个业务方法时,应该打开一个新的
SqlSession
,并在处理完请求或方法后立即关闭它。 - 这种方式可以避免不必要的数据库连接占用,同时保证了数据的一致性和隔离性。
- 局部作用域是最常见也是官方推荐的
SqlSession
管理方式。
2.全局作用域(Global Scope):
- 全局作用域下,
SqlSession
的生命周期与应用的生命周期相同。 - 在某些特殊场景下,可能需要将
SqlSession
存储在静态变量、单例或者应用级别的 Map 中,以便跨多个请求或方法共享同一个SqlSession
实例。 - 这种方式很少使用,因为它易于引起线程安全问题和数据一致性问题,通常不推荐在并发环境下使用。
- 如果必须使用全局作用域的
SqlSession
,那么需要开发者非常谨慎地管理事务和连接,确保每次操作后都正确提交或回滚事务,并在适当的时候关闭SqlSession
。
在实际应用中,为了保证性能和防止资源泄露,强烈建议采用局部作用域的方式管理 SqlSession
。而全局作用域的使用应当慎重考虑,确保不会引入线程安全和数据一致性等问题。在 Spring 集成 MyBatis 的场景下,SqlSession
的生命周期管理通常会交由 Spring 容器自动处理,大大简化了开发者的工作。
缓存的原理简述
- 查询过程:当执行查询时,MyBatis 首先查找一级缓存,如果没有找到,再去查找二级缓存;如果两级缓存都没有命中,才会执行 SQL 查询数据库。
- 更新过程:当执行增删改操作时,为维护数据的一致性,MyBatis 会清空一级缓存和受影响的二级缓存。
通过以上机制,MyBatis 的缓存能够有效地减少数据库的访问次数,从而提高应用的性能。然而,需要合理地配置和使用缓存,避免因缓存数据过期或不一致而引发的问题。
在实际项目中使用 MyBatis 二级缓存时,需要通过一些配置和步骤来开启和使用。以下是使用 MyBatis 二级缓存的一般步骤:
步骤 1:依赖引入
确保你的项目中引入了 MyBatis 和对应的缓存实现库。如果使用的是 Maven,可以在 pom.xml
中添加如下依赖(版本号仅供参考,使用时请选用适当的版本):
xml复制代码
代码语言:javascript复制<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- 使用EhCache作为二级缓存实现 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>
步骤 2:MyBatis 配置文件中开启二级缓存
在 MyBatis 的全局配置文件(mybatis-config.xml
)中添加二级缓存的配置。需要设置 cacheEnabled
属性为 true
,这个属性默认为 true
,但最好在配置文件中明确指出。
xml复制代码
代码语言:javascript复制<configuration>
<settings>
<!-- 开启全局二级缓存,默认为true -->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
步骤 3:在 Mapper.xml 中配置二级缓存
在你希望启用二级缓存的 Mapper.xml 文件中添加 <cache>
标签。例如:
xml复制代码
代码语言:javascript复制<mapper namespace="com.example.mapper.UserMapper">
<!-- 开启这个Mapper的二级缓存 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
<!-- Mapper接口方法的定义 -->
</mapper>
eviction
:缓存的回收策略,如LRU
(最近最少使用的)、FIFO
(先进先出)等。flushInterval
:缓存刷新间隔,单位毫秒。size
:引用数目,缓存中对象的最大数量。readOnly
:是否只读。只读缓存会给所有调用者返回缓存对象的相同实例,因此它们不应该修改这些对象。
步骤 4:实体类序列化
由于二级缓存数据需要在不同的会话之间共享,MyBatis 会将缓存数据序列化后存储。因此,对应的实体类需要实现 java.io.Serializable
接口:
java复制代码
代码语言:javascript复制public class User implements Serializable {
private static final long serialVersionUID = 1L;
// 类的属性和方法
}
注意事项
- 二级缓存的使用场合:二级缓存适用于读多写少的情况,如果业务场景中数据频繁变动,可能导致缓存频繁失效,反而降低性能。
- 数据一致性:在使用二级缓存时,应注意缓存数据的一致性问题。在进行数据更新操作后,需要合理地清理相关缓存,避免出现脏读。
- 缓存策略的选择:可以根据实际需求选择合适的缓存回收策略和缓存配置。
通过以上步骤和注意事项,可以在实际项目中有效地配置和使用 MyBatis 的二级缓存,从而提高应用的性能。