Ehcache 是成熟的 Java 進程內緩存框架,支持堆內/堆外/磁盤三級存儲,與 MyBatis 無縫整合可大幅降低數據庫查詢壓力。本文通過完整實戰演示,從依賴引入、配置文件編寫、實體類序列化、Mapper 配置到效果驗證,構建「開箱即用」的二級緩存方案。
<dependencies>
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- MyBatis-Ehcache 整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
<!-- Ehcache 核心 (由 mybatis-ehcache 傳遞引入) -->
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<!-- 默認緩存策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<!-- 磁盤存儲路徑 (生產環境請修改為持久化目錄) -->
<diskStore path="D:/cache" /> <!-- Windows 開發環境 -->
<!-- --> <!-- Linux 生產環境 -->
</ehcache>
關鍵屬性解析:
| 屬性 | 含義 | 建議值 |
|---|---|---|
| maxElementsInMemory | 內存最大對象數 | 根據業務量級設置,如 10000-100000 |
| eternal | 是否永不過期(true 時忽略 TTL/TTI) | false(需配合 TTL/TTI) |
| timeToIdleSeconds (TTI) | 最大閒置時間,超過則過期 | 300-600 秒 |
| timeToLiveSeconds (TTL) | 最大生存時間,超過則過期 | 600-1800 秒 |
| overflowToDisk | 內存不足時是否溢寫磁盤 | true(需配置 diskStore) |
| memoryStoreEvictionPolicy | 淘汰策略:LRU/LFU/FIFO | LRU(最近最少使用) |
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 必須顯式聲明,避免反序列化失敗
private Long id;
private String username;
private String email;
// getter/setter/toString...
}
核心要求:
java.io.Serializable 接口serialVersionUID(禁止依賴 IDE 自動生成,跨 JVM 部署會不一致)<mapper namespace="com.example.mapper.UserMapper">
<!-- 方式一:標準 Ehcache 實現(推薦) -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
<!-- 方式二:帶日志輸出(調試用) -->
<!-- -->
<select id="selectById" resultType="User" useCache="true">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="selectAll" resultType="User" useCache="true">
SELECT * FROM users
</select>
<!-- 寫操作自動觸發緩存刷新 -->
<insert id="insert" parameterType="User" flushCache="true">
INSERT INTO users(username, email) VALUES(#{username}, #{email})
</insert>
<update id="update" parameterType="User" flushCache="true">
UPDATE users SET username=#{username}, email=#{email} WHERE id=#{id}
</update>
<delete id="deleteById" parameterType="Long" flushCache="true">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
關鍵配置點:
type="org.mybatis.caches.ehcache.EhcacheCache":指定緩存實現類useCache="true":查詢語句啟用緩存(默認 true,可顯式關閉)flushCache="true":增刪改語句執行後刷新緩存(默認 true)@Test
public void testEhcache() {
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession(); // 不同 Session 模擬不同請求
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
// 第一次查詢 - 觸發 SQL
User user1 = mapper1.selectById(1L);
System.out.println("第一次查詢: " + user1);
session1.close(); // 關鍵:MyBatis 二級緩存需提交/關閉 Session 才寫入
// 第二次查詢 - 命中緩存
User user2 = mapper2.selectById(1L);
System.out.println("第二次查詢: " + user2);
session2.close();
// 驗證緩存命中
Assert.assertSame(user1, user2); // 同一緩存對象引用
}
==> Preparing: SELECT * FROM users WHERE id = ?
==> Parameters: 1(Long)
<== Columns: id, username, email
<== Row: 1, zhangsan, zhangsan@example.com
第一次查詢: User(id=1, username=zhangsan, email=zhangsan@example.com)
第二次查詢: User(id=1, username=zhangsan, email=zhangsan@example.com)
配置 overflowToDisk="true" 後,查看 D:/cache 目錄:
D:\cache
├── com.example.mapper.UserMapper.data (緩存數據文件)
├── com.example.mapper.UserMapper.index (緩存索引文件)
└── ehcache.lock (鎖文件)
<!-- ehcache.xml 中添加命名緩存 -->
<cache name="com.example.mapper.UserMapper"
maxElementsInMemory="5000"
timeToIdleSeconds="180"
timeToLiveSeconds="300"
overflowToDisk="true"
eternal="false"
memoryStoreEvictionPolicy="LRU" />
<cache name="com.example.mapper.OrderMapper"
maxElementsInMemory="20000"
timeToIdleSeconds="60"
timeToLiveSeconds="120"
overflowToDisk="false"
eternal="false"
memoryStoreEvictionPolicy="LFU" />
Mapper 中引用:<cache-ref namespace="com.example.mapper.UserMapper" />
// 程序化獲取統計信息
CacheManager cacheManager = CacheManager.getInstance();
Ehcache cache = cacheManager.getEhcache("com.example.mapper.UserMapper");
System.out.println("命中率: " + cache.getStatistics().getCacheHitPercentage() + "%");
System.out.println("內存命中: " + cache.getStatistics().getInMemoryHits());
System.out.println("磁盤命中: " + cache.getStatistics().getOnDiskHits());
System.out.println("未命中: " + cache.getStatistics().getCacheMisses());
System.out.println("當前大小: " + cache.getSize() + " / " + cache.getMaxElementsInMemory());
serialVersionUID 完全一致new User()),設置較短 TTLtimeToLiveSeconds="300 + random(60)"),避免大量 Key 同時過期maxElementsInMemory,啟用 overflowToDisk,監控 Ehcache 統計指標Ehcache + MyBatis 整合僅需「一個依賴、一個配置文件、一行 Mapper 配置、實體類序列化」四步即可落地。通過合理的緩存策略(TTL/TTI/淘汰算法/磁盤溢寫)、監控指標觀測、生產環境避坑指南,可構建高可用、高性能的二級緩存層,將數據庫 QPS 壓力降低 80% 以上。建議團隊建立「緩存設計規範」,新表上線必填緩存策略文檔,將緩存治理從事後補救轉為事前設計。