Redis缓存雪崩:如何通过锁和TTL抖动来预防

了解过期缓存键的请求激增如何压垮数据库,以及如何在Redis中使用互斥锁、概率性提前重算和TTL抖动来阻止这种情况。

Redis缓存雪崩:如何通过锁和TTL抖动来预防

什么是缓存雪崩?

缓存雪崩是指大量缓存键在同一时间过期,导致所有请求直接打到数据库,造成数据库负载激增甚至崩溃的现象。

解决方案

1. 互斥锁(Mutex Lock)

使用Redis的SETNX命令实现互斥锁,确保只有一个请求去重建缓存:

import redis

r = redis.Redis()

def get_data(key):
    # 尝试获取缓存
    data = r.get(key)
    if data is not None:
        return data
    
    # 尝试获取锁
    lock_key = f"lock:{key}"
    if r.setnx(lock_key, "1"):
        # 设置锁过期时间,防止死锁
        r.expire(lock_key, 10)
        
        # 从数据库获取数据
        data = query_database(key)
        
        # 设置缓存
        r.setex(key, 3600, data)
        
        # 释放锁
        r.delete(lock_key)
        
        return data
    else:
        # 等待并重试
        time.sleep(0.1)
        return get_data(key)

2. 概率性提前重算

在缓存过期前,以一定概率提前重建缓存:

import random

def get_data_with_probability(key):
    data = r.get(key)
    if data is not None:
        # 检查是否需要提前重算
        ttl = r.ttl(key)
        if ttl < 300 and random.random() < 0.1:
            # 异步重建缓存
            rebuild_cache_async(key)
        return data
    
    # 缓存不存在,正常获取
    return rebuild_cache(key)

3. TTL抖动

给缓存过期时间添加随机值,避免大量键同时过期:

import random

def set_cache_with_jitter(key, data, base_ttl=3600):
    # 添加±30%的随机抖动
    jitter = random.uniform(0.7, 1.3)
    ttl = int(base_ttl * jitter)
    r.setex(key, ttl, data)

最佳实践

  1. 组合使用:将互斥锁和TTL抖动结合使用效果最佳
  2. 设置合理的过期时间:避免所有缓存同时过期
  3. 使用缓存预热:系统启动时预先加载热点数据
  4. 监控告警:实时监控缓存命中率和数据库负载

总结

缓存雪崩是分布式系统中常见的问题,通过互斥锁、概率性提前重算和TTL抖动可以有效预防。选择适合业务场景的方案,结合监控和告警机制,可以确保系统在高并发下的稳定性。