Redis 缓存问题详解及解决方案

一、缓存击穿 (Cache Breakdown)

原理
某个热点 Key 突然过期,同时大量并发请求该 Key,导致请求直接穿透缓存击穿到数据库。

解决方案

  1. 互斥锁 (Mutex Lock)
    当缓存失效时,仅允许一个线程重建缓存,其他线程等待。
  2. 逻辑过期
    不设置物理过期时间,在 Value 中存储过期时间戳,异步更新缓存。

Python 实现 (互斥锁方案)

import redis
import time
import threading

r = redis.Redis()

def get_data(key):
    # 1. 尝试从缓存读取
    data = r.get(key)
    if data:
        return data
    
    # 2. 尝试获取锁 (SETNX + EXPIRE)
    lock_key = f"lock:{key}"
    if r.set(lock_key, "locked", nx=True, ex=5):  # 设置5秒锁过期
        try:
            # 3. 模拟数据库查询
            data_from_db = "Database Result"  # 真实场景需查DB
            # 4. 写入缓存
            r.setex(key, 60, data_from_db)  # 缓存60秒
            return data_from_db
        finally:
            r.delete(lock_key)
    else:
        # 5. 未获锁则短暂等待重试
        time.sleep(0.1)
        return get_data(key)  # 递归重试

# 测试并发
def test_breakdown():
    key = "hot_key"
    threads = []
    for i in range(10):  # 模拟10个并发请求
        t = threading.Thread(target=lambda: print(get_data(key)))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

test_breakdown()

二、缓存穿透 (Cache Penetration)

原理
请求访问数据库中不存在的数据(如非法ID),导致每次请求都穿透到数据库。

解决方案

  1. 布隆过滤器 (Bloom Filter)
    快速判断 Key 是否可能存在数据库中。
  2. 缓存空对象
    对不存在的数据也进行缓存(设置短过期时间)。

Python 实现 (缓存空对象)

def get_data_penetration(key):
    # 1. 尝试从缓存读取
    data = r.get(key)
    if data is not None:
        return None if data == b'NULL' else data  # 处理空值
    
    # 2. 查询数据库 (模拟不存在)
    data_from_db = None  # 假设数据库返回空
    
    if data_from_db is None:
        # 3. 缓存空对象 (5分钟过期)
        r.setex(key, 300, 'NULL')  # 特殊值标记
        return None
    else:
        r.setex(key, 60, data_from_db)
        return data_from_db

三、缓存雪崩 (Cache Avalanche)

原理
大量 Key 同时过期,导致所有请求穿透到数据库,引发连锁故障。

解决方案

  1. 随机过期时间
    基础过期时间 + 随机值,分散 Key 过期时间。
  2. 热点数据永不过期
    后台异步更新缓存。
  3. 多级缓存架构
    本地缓存 + Redis 缓存。

Python 实现 (随机过期时间)

import random

def set_data(key, value):
    # 基础过期时间 + 随机偏移 (0-300秒)
    expire = 3600 + random.randint(0, 300)
    r.setex(key, expire, value)

# 批量设置缓存时使用
set_data("key1", "value1")
set_data("key2", "value2")

四、缓存预热 (Cache Warm-up)

原理
系统上线前预先加载热点数据到缓存,避免冷启动时大量请求穿透到数据库。

实现方式

  1. 手动触发脚本加载数据
  2. 定时任务预热
  3. 启动时自动加载

Python 实现

def cache_warm_up():
    hot_keys = ["news:1", "product:100", "top:list"]  # 预定义热点Key
    
    for key in hot_keys:
        if not r.exists(key):
            # 从数据库加载数据
            data = f"Data for {key}"  # 真实场景需查DB
            r.setex(key, 3600, data)  # 缓存1小时
            print(f"Preloaded: {key}")

# 系统启动时执行
cache_warm_up()

总结对比表

问题核心原因解决方案关键实现技术
缓存击穿热点 Key 突然失效互斥锁、逻辑过期SETNX + EXPIRE
缓存穿透查询不存在的数据布隆过滤器、缓存空对象特殊值标记 (如’NULL’)
缓存雪崩大量 Key 同时过期随机过期时间、多级缓存基础过期时间 + 随机值
缓存预热冷启动时无缓存启动时加载热点数据初始化脚本加载

最佳实践:生产环境中建议组合使用多种方案(如布隆过滤器+空对象+互斥锁+随机过期时间),并配合监控系统实时检测缓存命中率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值