Redis集群架构设计与缓存穿透雪崩击穿防护实战

Redis集群模式选型与架构设计要点

Redis提供三种集群模式:主从复制(读写分离)、哨兵模式(自动故障转移)和Cluster模式(数据分片+高可用)。生产环境数据量超过单机内存或QPS超过单节点承载能力时,必须使用Cluster模式。Redis Cluster将16384个哈希槽分配到多个主节点,每个key通过CRC16(key)%16384确定所属槽位。

架构设计核心要点:主节点至少3个(每个负责约5461个槽),每个主节点配1个从节点;客户端使用Smart Client(Jedis Cluster / Lettuce)缓存槽位映射,避免MOVED重定向开销;跨机房部署时同主从放在同机房减少网络延迟。

Redis Cluster部署与配置实战

# redis.conf 集群配置
port 6379
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-announce-ip 10.0.1.11
cluster-announce-port 6379
cluster-announce-bus-port 16379

# 内存与持久化配置
maxmemory 16gb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec

# 启动6个节点(3主3从)后创建集群
redis-cli --cluster create \
  10.0.1.11:6379 10.0.1.12:6379 10.0.1.13:6379 \
  10.0.1.14:6379 10.0.1.15:6379 10.0.1.16:6379 \
  --cluster-replicas 1

# 验证集群状态
redis-cli cluster info

缓存穿透防护:布隆过滤器与空值缓存策略

缓存穿透指大量请求查询数据库中根本不存在的数据,请求绕过缓存直接打到数据库。典型场景:恶意攻击请求随机ID、爬虫遍历不存在的URL。

// 布隆过滤器方案(Java + RedisBloom)
// 1. 服务启动时将所有合法ID加载到布隆过滤器
BloomFilter<String> bloomFilter = bloomFilterOps.createFilter(
    "valid_ids", 10000000L, 0.01);  // 1000万ID,1%误判率

// 全量加载合法ID
List<String> allIds = productMapper.selectAllIds();
allIds.forEach(id -> bloomFilterOps.add("valid_ids", id));

// 2. 查询时先校验布隆过滤器
public Product getProduct(String id) {
    if (!bloomFilterOps.contains("valid_ids", id)) {
        return null;
    }

    String cacheKey = "product:" + id;
    String cached = jedis.get(cacheKey);

    if (cached != null) {
        if ("NULL".equals(cached)) return null;
        return JSON.parseObject(cached, Product.class);
    }

    Product product = productMapper.selectById(id);
    if (product == null) {
        jedis.setex(cacheKey, 60, "NULL");
        return null;
    }

    jedis.setex(cacheKey, 3600, JSON.toJSONString(product));
    return product;
}

缓存雪崩防护:随机过期与熔断降级方案

缓存雪崩指大量key同一时刻过期,请求瞬间全部打到数据库。常见原因:批量设置相同TTL、缓存服务整体宕机。

// 随机TTL防批量过期
public void setWithRandomTTL(String key, String value, int baseSeconds) {
    int randomOffset = ThreadLocalRandom.current().nextInt(baseSeconds / 10);
    jedis.setex(key, baseSeconds + randomOffset, value);
}

// 批量预热数据时分散TTL
public void warmUpCache(List<Product> products) {
    Pipeline pipeline = jedis.pipelined();
    for (Product p : products) {
        String key = "product:" + p.getId();
        int ttl = 3600 + ThreadLocalRandom.current().nextInt(360);
        pipeline.setex(key, ttl, JSON.toJSONString(p));
    }
    pipeline.sync();
}

// Hystrix熔断降级保护数据库
@HystrixCommand(
    fallbackMethod = "getProductFallback",
    commandProperties = {
        @HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="20"),
        @HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="50"),
        @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="5000")
    }
)
public Product getProductWithCircuitBreaker(String id) {
    return getProduct(id);
}

public Product getProductFallback(String id) {
    return Product.defaultProduct(id);
}

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

缓存击穿指热点key过期的瞬间,大量并发请求同时打到数据库重建缓存。与雪崩不同,击穿是单个key的问题,但该key是热点数据。

// 方案1:互斥锁(Redis SETNX实现)
public Product getProductWithMutex(String id) {
    String cacheKey = "product:" + id;
    String lockKey = "lock:product:" + id;
    String cached = jedis.get(cacheKey);

    if (cached != null) {
        return "NULL".equals(cached) ? null : JSON.parseObject(cached, Product.class);
    }

    if ("OK".equals(jedis.set(lockKey, "1", "NX", "EX", 10))) {
        try {
            cached = jedis.get(cacheKey);
            if (cached != null) return JSON.parseObject(cached, Product.class);

            Product product = productMapper.selectById(id);
            int ttl = product != null ? 3600 : 60;
            String val = product != null ? JSON.toJSONString(product) : "NULL";
            jedis.setex(cacheKey, ttl, val);
            return product;
        } finally {
            jedis.del(lockKey);
        }
    } else {
        Thread.sleep(50);
        return getProductWithMutex(id);
    }
}

// 方案2:逻辑过期(不设TTL,数据内嵌过期时间)
public Product getProductWithLogicalExpire(String id) {
    String cacheKey = "product:logic:" + id;
    String cached = jedis.get(cacheKey);

    if (cached == null) return rebuildCache(id);

    CacheData cacheData = JSON.parseObject(cached, CacheData.class);
    if (cacheData.getExpireTime().isAfter(LocalDateTime.now())) {
        return cacheData.getProduct();
    }

    if ("OK".equals(jedis.set("lock:rebuild:" + id, "1", "NX", "EX", 10))) {
        CompletableFuture.runAsync(() -> rebuildCache(id));
    }
    return cacheData.getProduct();
}

三种缓存问题的防护策略总结:穿透用布隆过滤器+空值缓存、雪崩用随机TTL+熔断降级、击穿用互斥锁+逻辑过期。生产环境应同时部署三种防护机制,Redis Cluster配合多级缓存(L1本地Caffeine + L2 Redis)是高并发场景的标准架构方案。

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

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

相关推荐