Python 操作 Redis:缓存穿透、击穿与雪崩的解决方案与代码实现

缓存穿透的解决方案与代码实现

缓存穿透是指查询一个不存在的数据,由于缓存未命中,请求直接穿透到数据库,导致数据库压力过大。解决方案包括布隆过滤器和缓存空对象。

布隆过滤器是一种概率型数据结构,用于快速判断某个元素是否存在于集合中。可以使用 pybloom_live 库实现:

from pybloom_live import ScalableBloomFilter
import redis

# 初始化布隆过滤器
bloom_filter = ScalableBloomFilter(initial_capacity=1000, error_rate=0.001)

# 将存在的键添加到布隆过滤器
bloom_filter.add("existing_key")

# 查询前先检查布隆过滤器
key = "non_existent_key"
if key not in bloom_filter:
    print("Key does not exist")
else:
    r = redis.StrictRedis()
    value = r.get(key)
    if value is None:
        r.setex(key, 60, "")  # 缓存空对象,设置短过期时间

缓存空对象是在查询数据库未命中时,将空结果缓存到 Redis 中:

def get_data(key):
    r = redis.StrictRedis()
    value = r.get(key)
    if value is not None:
        return value.decode() if value else None
    
    # 模拟数据库查询
    db_value = query_database(key)
    if db_value is None:
        r.setex(key, 60, "")  # 缓存空对象
        return None
    else:
        r.setex(key, 3600, db_value)  # 缓存真实数据
        return db_value

缓存击穿的解决方案与代码实现

缓存击穿是指某个热点数据过期后,大量请求同时涌入数据库。解决方案包括互斥锁和永不过期策略。

互斥锁通过 Redis 的 SETNX 命令实现:

def get_data_with_lock(key):
    r = redis.StrictRedis()
    value = r.get(key)
    if value is not None:
        return value.decode()
    
    lock_key = f"lock:{key}"
    if r.setnx(lock_key, 1):  # 获取锁
        r.expire(lock_key, 10)  # 设置锁过期时间
        try:
            db_value = query_database(key)
            if db_value is None:
                r.setex(key, 60, "")  # 缓存空对象
            else:
                r.setex(key, 3600, db_value)
            return db_value
        finally:
            r.delete(lock_key)  # 释放锁
    else:
        time.sleep(0.1)  # 短暂等待后重试
        return get_data_with_lock(key)

永不过期策略通过异步更新缓存实现:

def get_data_with_async_update(key):
    r = redis.StrictRedis()
    value = r.get(key)
    if value is not None:
        # 异步更新缓存
        if random.random() < 0.1:  # 10% 概率触发更新
            threading.Thread(target=async_update_cache, args=(key,)).start()
        return value.decode()
    
    db_value = query_database(key)
    if db_value is None:
        r.setex(key, 60, "")
    else:
        r.setex(key, 3600, db_value)
    return db_value

def async_update_cache(key):
    db_value = query_database(key)
    if db_value is not None:
        r = redis.StrictRedis()
        r.setex(key, 3600, db_value)

缓存雪崩的解决方案与代码实现

缓存雪崩是指大量缓存同时过期,导致请求直接打到数据库。解决方案包括随机过期时间和多级缓存。

随机过期时间通过为缓存设置不同的过期时间来避免同时失效:

def set_cache_with_random_expire(key, value):
    r = redis.StrictRedis()
    expire_time = 3600 + random.randint(0, 600)  # 3600-4200 秒随机过期
    r.setex(key, expire_time, value)

多级缓存通过组合本地缓存和 Redis 缓存实现:

from cachetools import TTLCache

# 本地缓存
local_cache = TTLCache(maxsize=1000, ttl=60)

def get_data_with_multilevel_cache(key):
    # 先查本地缓存
    value = local_cache.get(key)
    if value is not None:
        return value
    
    # 再查 Redis
    r = redis.StrictRedis()
    value = r.get(key)
    if value is not None:
        value = value.decode()
        local_cache[key] = value  # 写入本地缓存
        return value
    
    # 查数据库
    db_value = query_database(key)
    if db_value is None:
        r.setex(key, 60, "")
        local_cache[key] = ""
    else:
        r.setex(key, 3600, db_value)
        local_cache[key] = db_value
    return db_value

总结

针对缓存穿透问题,布隆过滤器和缓存空对象是有效解决方案。对于缓存击穿,互斥锁和永不过期策略能够避免大量请求同时访问数据库。处理缓存雪崩时,随机过期时间和多级缓存可以分散请求压力。通过合理组合这些技术,能够显著提升系统的稳定性和性能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐