Redis 7.x多级缓存架构设计与缓存击穿雪崩防御方案

数据库运维中的缓存架构选型与Redis 7.x新特性

数据库运维实践中,缓存层是读写性能的分水岭。一个高并发读场景直接查MySQL,QPS上限约3000(单实例),加Redis缓存后读QPS可达5万+。但缓存不是简单加一层Redis就完事——缓存击穿、缓存雪崩、数据一致性是三大核心难题。Redis 7.x引入了Function特性(替代Lua脚本)、Multi-part AOF和ACL v2,为多级缓存架构提供了更可靠的底层支撑。

多级缓存架构设计:本地缓存+Redis+数据库分层

多级缓存的核心思路是在请求链路上设置多层缓存,逐层拦截热点查询。典型三层架构:

请求 → 本地缓存(Caffeine) → Redis集群 → MySQL主从

本地缓存层配置(Spring Boot集成Caffeine):

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofMinutes(5))
            .recordStats());
        return manager;
    }

    @Bean
    public RedisCacheManager redisCacheManager(
            RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration
            .defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer())
            );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }
}

本地缓存5分钟过期,Redis缓存30分钟过期,两个过期时间必须错开。本地缓存处理极热点数据(如首页配置),Redis缓存处理中频查询,MySQL兜底。本地缓存命中率目标40%+,Redis命中率85%+,整体缓存命中率95%以上。

缓存击穿防御:互斥锁与逻辑过期双方案

缓存击穿指高并发请求同时穿透到数据库查同一条热点数据。两种防御方案:

方案一:Redis分布式锁(Mutex Lock)

public String getWithMutex(String key) {
    String value = redisTemplate.opsForValue().get(key);
    if (value != null) {
        return value;
    }

    String lockKey = "lock:" + key;
    try {
        // 尝试获取互斥锁,等待3秒
        boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (locked) {
            // 双重检查
            value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                return value;
            }
            // 查数据库
            value = queryFromDB(key);
            redisTemplate.opsForValue()
                .set(key, value, 30, TimeUnit.MINUTES);
        } else {
            // 等待重试
            Thread.sleep(50);
            return getWithMutex(key);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        redisTemplate.delete(lockKey);
    }
    return value;
}

方案二:逻辑过期(Logical Expiry)

缓存永不过期(TTL设为永久),数据内嵌逻辑过期时间。读取时检查逻辑过期时间,过期则异步刷新:

@Data
public class CacheData<T> {
    private T data;
    private LocalDateTime expireTime;
}

public T getWithLogicalExpire(String key) {
    CacheData<T> cache = (CacheData<T>) redisTemplate
        .opsForValue().get(key);
    
    if (cache == null) {
        return null;
    }
    
    if (cache.getExpireTime().isAfter(LocalDateTime.now())) {
        return cache.getData();
    }
    
    // 逻辑过期,触发异步刷新
    String lockKey = "lock:" + key;
    if (redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS)) {
        CompletableFuture.runAsync(() -> {
            try {
                T freshData = queryFromDB(key);
                CacheData<T> newCache = new CacheData<>();
                newCache.setData(freshData);
                newCache.setExpireTime(
                    LocalDateTime.now().plusMinutes(30));
                redisTemplate.opsForValue().set(key, newCache);
            } finally {
                redisTemplate.delete(lockKey);
            }
        });
    }
    // 返回旧数据,用户无感知
    return cache.getData();
}

逻辑过期方案的代价是短暂的数据不一致窗口,但优势是永远不会击穿到数据库。电商秒杀等极端高并发场景优先用逻辑过期,数据一致性要求高的场景用互斥锁。

缓存雪崩防御:随机TTL与熔断降级

缓存雪崩指大量缓存key在同一时刻过期,瞬间大量请求涌入数据库。防御手段:TTL添加随机偏移量。

public void setWithRandomTTL(String key, Object value, 
                              long baseMinutes) {
    long randomOffset = ThreadLocalRandom.current()
        .nextLong(0, baseMinutes / 2);
    long ttl = baseMinutes + randomOffset;
    redisTemplate.opsForValue()
        .set(key, value, ttl, TimeUnit.MINUTES);
}

// 基础TTL 30分钟 + 0~15分钟随机偏移
setWithRandomTTL("user:profile:123", data, 30);

加上熔断降级策略——当Redis集群不可用时,通过Hystrix或Sentinel的熔断器快速返回降级数据,防止请求全部打到MySQL:

@SentinelResource(value = "getUserProfile",
    fallback = "getUserProfileFallback")
public User getUserProfile(Long userId) {
    return cacheService.getWithLogicalExpire(
        "user:profile:" + userId);
}

public User getUserProfileFallback(Long userId) {
    // 返回兜底数据或静态页面
    return User.defaultProfile(userId);
}

Redis 7.x的Function特性可以将互斥锁+逻辑过期封装为Redis Function,原子性比Lua更可控。数据库高可用架构中,缓存层的健壮性直接决定了整体系统的抗压能力——缓存设计不是可选优化,是必做项。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/redis7x-duo-ji-huan-cun-jia-gou-she-ji-yu-huan-cun-ji-chuan/

(0)
小编小编
上一篇 6小时前
下一篇 6小时前

相关推荐