手写MyBatis第79弹:MyBatis二级缓存事务一致性:解决脏读与缓存一致性难题
MyBatis二级缓存完整集成:CachingExecutor与TransactionalCacheManager源码级实现
手写MyBatis二级缓存事务集成:解决脏读与缓存一致性难题
二级缓存与执行器深度整合:从Mapper配置到事务管理的完整链路
CachingExecutor设计精髓:如何实现事务安全的二级缓存
MyBatis二级缓存事务一致性:TransactionalCacheManager机制详解
目录
正文
一、二级缓存集成的架构全景
二、Mapper配置与缓存实例创建
1. Mapper XML配置解析
2. Configuration中的缓存构建
三、CachingExecutor:二级缓存的执行入口
1. 装饰器模式的应用
2. 更新操作的缓存清理
四、TransactionalCacheManager:事务一致性核心
1. 事务性缓存的必要性
2. 核心实现机制
3. TransactionalCache的事务控制
五、SqlSessionFactory的集成改造
1. Executor创建逻辑
2. MappedStatement缓存配置关联
六、二级缓存的生命周期与失效策略
1. 缓存生命周期管理
2. 基于命名空间的缓存失效
七、事务边界与缓存一致性保障
1. SqlSession事务管理集成
2. 跨命名空间缓存清理
八、生产环境配置建议
1. 缓存配置策略
2. 监控与调优
九、总结
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我。文末有免费源码
免费获取源码。
更多内容敬请期待。如有需要可以联系作者免费送
更多源码定制,项目修改,项目二开可以联系作者
点击可以进行搜索(每人免费送一套代码):千套源码目录(点我)2025元旦源码免费送(点我)
我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。
正文
一、二级缓存集成的架构全景
二级缓存的核心挑战在于如何与MyBatis现有的执行流程无缝集成,同时保证事务一致性。这需要设计一个精巧的架构,将缓存逻辑嵌入到SQL执行的生命周期中。
集成核心组件关系:
-
CachingExecutor
:作为执行器的装饰器,拦截所有数据库操作 -
TransactionalCacheManager
:管理事务期间的缓存状态 -
MappedStatement
:承载缓存的配置信息 -
SqlSession
:根据配置决定是否启用缓存执行器
二、Mapper配置与缓存实例创建
1. Mapper XML配置解析
<!-- UserMapper.xml --><mapper namespace="com.example.UserMapper"><cache eviction="LRU"flushInterval="60000"size="1000"readOnly="true"/><select id="selectById" parameterType="long" resultType="User" useCache="true">SELECT * FROM user WHERE id = #{id}</select></mapper>
配置参数详解:
-
eviction
:淘汰策略(LRU、FIFO等) -
flushInterval
:自动刷新间隔(毫秒) -
size
:缓存最大容量 -
readOnly
:是否只读(性能与安全权衡)
2. Configuration中的缓存构建
public class Configuration {private final Map<String, Cache> caches = new HashMap<>();public void addCache(String namespace, Cache cache) {caches.put(namespace, cache);}public Cache getCache(String namespace) {return caches.get(namespace);}// 解析cache配置并创建Cache实例public Cache buildCache(String namespace, Properties properties) {String eviction = properties.getProperty("eviction", "LRU");int size = Integer.parseInt(properties.getProperty("size", "1024"));boolean readOnly = Boolean.parseBoolean(properties.getProperty("readOnly", "false"));Cache cache = new PerpetualCache(namespace);// 应用装饰器链if ("LRU".equals(eviction)) {cache = new LruCache(cache, size);}if (!readOnly) {cache = new SerializedCache(cache);}cache = new SynchronizedCache(cache);cache = new LoggingCache(cache);addCache(namespace, cache);return cache;}}
三、CachingExecutor:二级缓存的执行入口
1. 装饰器模式的应用
public class CachingExecutor implements Executor {private final Executor delegate;private final TransactionalCacheManager tcm = new TransactionalCacheManager();public CachingExecutor(Executor delegate) {this.delegate = delegate;}@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,CacheKey key, BoundSql boundSql) throws SQLException {// 检查是否启用二级缓存Cache cache = ms.getCache();if (cache != null && ms.isUseCache() && resultHandler == null) {// 尝试从二级缓存获取List<E> list = (List<E>) tcm.getObject(cache, key);if (list != null) {// 缓存命中return list;}// 缓存未命中,查询数据库list = delegate.query(ms, parameter, rowBounds, resultHandler, key, boundSql);// 将结果放入缓存(事务提交后生效)tcm.putObject(cache, key, list);return list;}// 未启用缓存,直接委托给底层执行器return delegate.query(ms, parameter, rowBounds, resultHandler, key, boundSql);}}
2. 更新操作的缓存清理
@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {// 执行更新前清理缓存if (ms.getCache() != null) {// 根据配置决定立即清理还是事务提交时清理if (ms.isFlushCacheRequired()) {tcm.clear(ms.getCache());}}// 委托给底层执行器执行更新return delegate.update(ms, parameter);}
四、TransactionalCacheManager:事务一致性核心
1. 事务性缓存的必要性
问题场景:
-
事务A查询数据(放入缓存但未提交)
-
事务B读取缓存(读到未提交的数据)
-
事务A回滚(缓存中留下脏数据)
TransactionalCacheManager
通过延迟提交机制解决这个问题。
2. 核心实现机制
public class TransactionalCacheManager {private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();public Object getObject(Cache cache, CacheKey key) {return getTransactionalCache(cache).getObject(key);}public void putObject(Cache cache, CacheKey key, Object value) {getTransactionalCache(cache).putObject(key, value);}public void clear(Cache cache) {getTransactionalCache(cache).clear();}public void commit() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.commit();}}public void rollback() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.rollback();}}private TransactionalCache getTransactionalCache(Cache cache) {return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);}}
3. TransactionalCache的事务控制
public class TransactionalCache implements Cache {private final Cache delegate;private final Map<Object, Object> entriesToAddOnCommit = new HashMap<>();private final Set<Object> entriesToRemoveOnCommit = new HashSet<>();private boolean clearOnCommit = false;public TransactionalCache(Cache delegate) {this.delegate = delegate;}@Overridepublic Object getObject(Object key) {// 事务期间只从底层缓存读取已提交的数据return delegate.getObject(key);}@Overridepublic void putObject(Object key, Object value) {// 暂存到待提交映射,事务提交后才真正放入缓存entriesToAddOnCommit.put(key, value);}@Overridepublic void clear() {clearOnCommit = true;entriesToAddOnCommit.clear();}public void commit() {if (clearOnCommit) {delegate.clear();}// 移除待删除的条目for (Object key : entriesToRemoveOnCommit) {delegate.removeObject(key);}// 添加待提交的条目for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {delegate.putObject(entry.getKey(), entry.getValue());}reset();}public void rollback() {reset();}private void reset() {clearOnCommit = false;entriesToAddOnCommit.clear();entriesToRemoveOnCommit.clear();}}
五、SqlSessionFactory的集成改造
1. Executor创建逻辑
public class DefaultSqlSessionFactory implements SqlSessionFactory {@Overridepublic SqlSession openSession() {return openSessionFromDataSource(null, false);}private SqlSession openSessionFromDataSource(TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = getTransaction(level, autoCommit);Executor executor = createExecutor(tx);return new DefaultSqlSession(executor);}private Executor createExecutor(Transaction tx) {Executor executor = new SimpleExecutor(tx);// 如果配置了缓存,包装成CachingExecutorif (configuration.isCacheEnabled()) {executor = new CachingExecutor(executor);}return executor;}}
2. MappedStatement缓存配置关联
public class MappedStatement {private String id;private Cache cache;private boolean useCache;private boolean flushCacheRequired;public boolean isUseCache() {return useCache && cache != null;}// Builder模式创建MappedStatementpublic static class Builder {public MappedStatement build() {// 关联对应的Cache实例if (cacheRef != null) {mappedStatement.cache = configuration.getCache(cacheRef);}return mappedStatement;}}}
六、二级缓存的生命周期与失效策略
1. 缓存生命周期管理
public class CacheLifecycleManager {private final ScheduledExecutorService scheduler;private final Map<Cache, ScheduledFuture> flushTasks = new HashMap<>();public void scheduleFlush(Cache cache, long flushInterval) {ScheduledFuture future = scheduler.scheduleAtFixedRate(() -> {cache.clear();}, flushInterval, flushInterval, TimeUnit.MILLISECONDS);flushTasks.put(cache, future);}public void stopFlush(Cache cache) {ScheduledFuture future = flushTasks.remove(cache);if (future != null) {future.cancel(false);}}
}
2. 基于命名空间的缓存失效
public class NamespaceCacheManager {private final Configuration configuration;public void clearNamespaceCache(String namespace) {Cache cache = configuration.getCache(namespace);if (cache != null) {cache.clear();}}// 根据表名清理相关命名空间的缓存public void clearCachesByTable(String tableName) {for (String namespace : getNamespacesByTable(tableName)) {clearNamespaceCache(namespace);}}
}
七、事务边界与缓存一致性保障
1. SqlSession事务管理集成
public class DefaultSqlSession implements SqlSession {private final Executor executor;@Overridepublic void commit() {try {executor.commit(true);// 提交事务性缓存if (executor instanceof CachingExecutor) {((CachingExecutor) executor).getTransactionalCacheManager().commit();}} catch (SQLException e) {throw new RuntimeException("提交失败", e);}}@Overridepublic void rollback() {try {executor.rollback(true);// 回滚事务性缓存if (executor instanceof CachingExecutor) {((CachingExecutor) executor).getTransactionalCacheManager().rollback();}} catch (SQLException e) {throw new RuntimeException("回滚失败", e);}}
}
2. 跨命名空间缓存清理
复杂业务场景下,一个更新操作可能影响多个Mapper的缓存:
public class CrossNamespaceCacheCleaner {private final Configuration configuration;public void onUpdate(String statementId, Object parameter) {String updatedNamespace = getNamespaceFromStatementId(statementId);Set<String> affectedNamespaces = analyzeAffectedNamespaces(updatedNamespace, parameter);for (String namespace : affectedNamespaces) {Cache cache = configuration.getCache(namespace);if (cache != null) {cache.clear();}}}
}
八、生产环境配置建议
1. 缓存配置策略
<!-- 读多写少的场景 -->
<cache eviction="LRU" size="5000" readOnly="true"/><!-- 读写均衡的场景 -->
<cache eviction="FIFO" size="1000" flushInterval="300000" readOnly="false"/><!-- 写多读少的场景 -->
<cache eviction="WEAK" size="500" readOnly="false"/>
2. 监控与调优
public class CacheMonitor {public void monitorCachePerformance() {for (Map.Entry<String, Cache> entry : configuration.getCaches().entrySet()) {if (entry.getValue() instanceof LoggingCache) {LoggingCache loggingCache = (LoggingCache) entry.getValue();double hitRatio = loggingCache.getHitRatio();if (hitRatio < 0.3) {// 命中率过低,考虑调整缓存策略logger.warn("缓存 {} 命中率过低: {}", entry.getKey(), hitRatio);}}}}}
九、总结
二级缓存的完整集成是一个系统工程,需要精心设计各个组件之间的协作关系。CachingExecutor
和TransactionalCacheManager
是实现事务安全缓存的核心机制。
关键设计要点:
-
装饰器模式实现执行器增强
-
事务性缓存确保数据一致性
-
灵活的配置机制支持不同场景
-
完善的生命周期管理避免内存泄漏
-
监控体系保障系统稳定运行
通过本文的完整实现方案,读者可以深入理解MyBatis二级缓存的工作原理,并能够在实际项目中正确配置和使用二级缓存。
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我。文末有免费源码
💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!💖常来我家多看看,
📕网址:扣棣编程,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!
往期文章推荐:
基于Springboot + vue实现的学生宿舍信息管理系统
免费获取宠物商城源码--SpringBoot+Vue宠物商城网站系统
【2025小年源码免费送】