Spring Boot集成Redis缓存:transactionAware为true时超时如何跳过缓存
问题根因
当你将RedisCacheManager的transactionAware设置为true时,缓存操作会被绑定到Spring事务同步流程中,在事务提交/回滚阶段执行,该场景下默认的CacheErrorHandler不会被触发,因此你之前的异常降级方案失效。
解决方案:自定义容错RedisCache实现
通过自定义RedisCache子类拦截所有缓存操作的异常,捕获Redis相关故障后直接跳过缓存逻辑,不向上抛出异常,即可实现不中断业务流程的需求,不受transactionAware配置影响。
实现步骤
- 自定义容错RedisCache类,重写所有缓存操作方法,捕获异常降级
import org.springframework.cache.Cache.ValueRetrievalException; import org.springframework.dao.QueryTimeoutException; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.Callable; public class FaultTolerantRedisCache extends RedisCache { private static final Logger log = LoggerFactory.getLogger(FaultTolerantRedisCache.class); protected FaultTolerantRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) { super(name, cacheWriter, cacheConfig); } @Override public ValueWrapper get(Object key) { try { return super.get(key); } catch (Exception e) { handleCacheError("get", key, e); return null; } } @Override public <T> T get(Object key, Class<T> type) { try { return super.get(key, type); } catch (Exception e) { handleCacheError("get", key, e); return null; } } @Override public <T> T get(Object key, Callable<T> valueLoader) { try { return super.get(key, valueLoader); } catch (Exception e) { handleCacheError("get", key, e); try { return valueLoader.call(); } catch (Exception ex) { throw new ValueRetrievalException(key, valueLoader, ex); } } } @Override public void put(Object key, Object value) { try { super.put(key, value); } catch (Exception e) { handleCacheError("put", key, e); } } @Override public void evict(Object key) { try { super.evict(key); } catch (Exception e) { handleCacheError("evict", key, e); } } @Override public void clear() { try { super.clear(); } catch (Exception e) { handleCacheError("clear", null, e); } } private void handleCacheError(String operation, Object key, Exception e) { // 只处理Redis相关异常,其他异常正常抛出避免业务异常被掩盖 if (e instanceof QueryTimeoutException || e.getCause() instanceof io.lettuce.core.RedisException) { log.warn("Redis缓存[{}]操作失败,key={},跳过缓存逻辑:{}", operation, key, e.getMessage()); } else { throw e; } } }
- 自定义RedisCacheManager,创建上述容错缓存实例
import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import java.util.Map; public class FaultTolerantRedisCacheManager extends RedisCacheManager { public FaultTolerantRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations); } @Override protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { return new FaultTolerantRedisCache(name, getCacheWriter(), cacheConfig != null ? cacheConfig : getDefaultCacheConfiguration()); } }
- 修改原配置类,替换默认的RedisCacheManager为自定义实现
@Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); return new FaultTolerantRedisCacheManager( cacheWriter, buildDefault(), buildFromSettings() ) // 保留原有的事务感知配置 .setTransactionAware(true) .build(); }
注意事项
- 缓存读取失败时返回null,相当于缓存未命中,会直接走原业务方法查询数据,符合降级预期
- 缓存写入/删除失败时直接跳过,不会影响主事务的提交执行
- 可以根据业务需要调整异常拦截范围,比如加入Redis连接异常、集群故障等异常的判断
内容的提问来源于stack exchange,提问作者bobanman
相关产品推荐
相关产品推荐

