你好,我是猿java。
为了提高数据访问的性能,MyBatis 采用了一级缓存和二级缓存的缓存机制,那么它们是如何工作的?这篇文章,我们将详细介绍这两种缓存机制及其工作原理、配置方式和使用场景。
首先,看一张原理图:
接着,我们来详细地分析它们。
- 一级缓存 {#1-一级缓存} =================
一级缓存(也叫本地缓存)是 MyBatis 默认开启的,是基于 SqlSession 级别的缓存。也就是说,在同一个 SqlSession 中,对于相同的查询,如果参数相同,MyBatis 会从一级缓存中直接获取数据,而不会再去执行数据库查询。
1.1 特点 {#1-1-特点}
- 作用范围 :同一个
SqlSession
实例。 - 默认开启:无需额外配置。
- 清空时机 :
- 执行
insert
、update
、delete
操作时,一级缓存会被清空。 - 调用
SqlSession.clearCache()
方法手动清空。
- 执行
1.2 工作原理 {#1-2-工作原理}
当使用同一个 SqlSession
执行相同的 SQL 查询时,MyBatis 会先检查一级缓存是否存在对应的查询结果。如果存在,则直接返回缓存中的结果;如果不存在,则执行数据库查询并将结果放入一级缓存。
使用示例 {#使用示例}
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| try (SqlSession session = sqlSessionFactory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); User user1 = mapper.getUserById(1); // 查询数据库,结果存入一级缓存 User user2 = mapper.getUserById(1); // 从一级缓存中获取结果 // user1 和 user2 指向同一个对象 }
|
- 二级缓存(全局缓存) {#2-二级缓存(全局缓存)} =============================
二级缓存是基于 namespace(通常对应于 Mapper 接口)的缓存,作用范围超出了 SqlSession
,同一个 mapper 的多个 SqlSession
共享一级缓存之外的缓存。二级缓存需要显式开启和配置。
2.1 特点 {#2-1-特点}
- 作用范围 :同一个 Mapper 的所有
SqlSession
实例。 - 默认关闭:需要在配置文件中手动开启。
- 缓存实现:需要配置缓存实现类,如 MyBatis 自带的缓存或第三方缓存(例如 Ehcache、Redis)。
- 序列化 :二级缓存中的对象需要序列化,以支持跨
SqlSession
共享。
2.2 启用步骤 {#2-2-启用步骤}
-
全局配置 :在全局配置文件
mybatis-config.xml
中开启二级缓存。|---------------------|-----------------------------------------------------------------------------------------------------------------| |
1 2 3 4 5 6
|<configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> ... </configuration>
| -
Mapper 配置 :在每个需要使用二级缓存的 Mapper XML 文件中配置
<cache>
标签。|-----------------|-------------------------------------------------------------------------------------------------| |
1 2 3 4
|<mapper namespace="com.example.mapper.UserMapper"> <cache/> <!-- SQL 语句定义 --> </mapper>
|或者使用自定义缓存实现:
|-----------|-----------------------------------------------------------------| |
1
|<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
| -
映射接口注解:可以通过注解方式配置缓存(适用于 MyBatis 3.2 及以上版本)。
|-----------------|-----------------------------------------------------------------| |
1 2 3 4
|@CacheNamespace public interface UserMapper { // 方法定义 }
|
2.3 缓存清理策略 {#2-3-缓存清理策略}
MyBatis 二级缓存使用的是基于读写的缓存策略,当涉及数据修改操作(insert
、update
、delete
)时,会清理相关 Mapper 的二级缓存,以保证数据一致性。
使用示例 {#使用示例-1}
|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12
| // 第一个 SqlSession try (SqlSession session1 = sqlSessionFactory.openSession()) { UserMapper mapper1 = session1.getMapper(UserMapper.class); User user1 = mapper1.getUserById(1); // 查询数据库,结果存入一级和二级缓存 session1.commit(); } // 第二个 SqlSession try (SqlSession session2 = sqlSessionFactory.openSession()) { UserMapper mapper2 = session2.getMapper(UserMapper.class); User user2 = mapper2.getUserById(1); // 从二级缓存中获取结果 }
|
- 缓存的高级配置 {#3-缓存的高级配置} =======================
3.1 缓存刷新策略 {#3-1-缓存刷新策略}
可以通过 <cache>
标签的属性来配置缓存的刷新策略,如:
eviction
:指定缓存的清理策略(默认 LRU 策略)。flushInterval
:指定缓存刷新时间间隔(单位:毫秒)。size
:指定缓存的大小。readOnly
:指定缓存是否为只读。
示例:
|-------------------|-----------------------------------------------------------------------------------|
| 1 2 3 4 5
| <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
|
3.2 使用第三方缓存 {#3-2-使用第三方缓存}
MyBatis 支持集成多种第三方缓存,如 Ehcache、Redis、Hazelcast 等。以 Ehcache 为例,配置步骤如下:
-
添加依赖:
|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
1 2 3 4 5 6 7 8 9 10
|<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.6</version> </dependency>
| -
配置 Mapper:
|-----------|-----------------------------------------------------------------| |
1
|<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
| -
配置 Ehcache:
创建 ehcache.xml
文件,配置缓存策略。
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| <ehcache> <cache name="com.example.mapper.UserMapper" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="false"/> </ehcache>
|
-
缓存使用注意事项 {#4-缓存使用注意事项} =========================
-
数据一致性:使用二级缓存时,需要确保缓存与数据库的数据一致性,特别是在分布式环境下,推荐使用分布式缓存解决方案。
-
对象可序列化 :二级缓存中的对象需要实现
Serializable
接口,以支持缓存的序列化和反序列化。 -
避免缓存穿透:对高频率的查询,可以适当配置缓存,从而减少数据库的压力。
-
缓存命中率:合理设计查询语句和缓存策略,提升缓存的命中率,优化性能。
-
总结 {#5-总结} =============
本文,我们详细地介绍了 MyBatis 的缓存策略,包括一级缓存适用于单次请求中的重复查询,而二级缓存适用于跨请求的共享缓存。在实际应用中,应根据具体业务需求和系统架构,选择合适的缓存策略和实现方式,以达到最佳的性能优化效果。
- 学习交流 {#6-学习交流} =================