导航菜单

热点处理

场景

读写分离上线后,系统运行正常。

但某天,一个突发新闻事件发生了:

事件:某重大新闻突发
影响:
- 新闻 API 调用量暴增 10 倍
- Redis 中某个热点 key 被频繁访问
- Redis 单节点压力过大

问题分析

我查了一下监控:

热点数据分布:
- 新闻"突发事件 A":占总请求的 60%
- 其他所有新闻:占 40%

Redis 访问统计:
- key "news:热点 A":每秒 5000 次访问
- 其他所有 key:每秒 3000 次访问
- 总计:每秒 8000 次

问题:单个热点 key 导致 Redis 单点压力

解决方案

1. 多级缓存

class CacheManager:
    """多级缓存管理"""

    def __init__(self):
        # L1: 应用内存缓存(本地)
        self.local_cache = {}
        self.local_cache_ttl = {}

        # L2: Redis 缓存(分布式)
        self.redis_cache = redis.Redis()

    def get(self, key):
        """获取数据(先本地后 Redis)"""

        # 1. 检查本地缓存
        if key in self.local_cache:
            if time.time() < self.local_cache_ttl[key]:
                return self.local_cache[key]
            else:
                del self.local_cache[key]

        # 2. 检查 Redis 缓存
        data = self.redis_cache.get(key)
        if data:
            # 更新本地缓存
            self.local_cache[key] = data
            self.local_cache_ttl[key] = time.time() + 60  # 本地缓存 1 分钟
            return data

        # 3. 缓存未命中
        return None

    def set(self, key, value, ttl):
        """设置数据(同时写入本地和 Redis)"""

        # 写入本地缓存
        self.local_cache[key] = value
        self.local_cache_ttl[key] = time.time() + 60

        # 写入 Redis
        self.redis_cache.setex(key, ttl, value)

cache_manager = CacheManager()

2. 热点 Key 分散

def get_hot_news(news_id):
    """获取热点新闻(使用 key 分散)"""

    # 计算分散的 key
    shard_count = 10  # 分散到 10 个 key
    shard_index = hash(news_id) % shard_count

    # 使用不同的 key
    cache_key = f'news:hot:{shard_index}:{news_id}'

    # 查询缓存
    data = cache_manager.get(cache_key)
    if data:
        return data

    # 调用 API
    data = fetch_news_from_db(news_id)

    # 写入缓存
    cache_manager.set(cache_key, data, ttl=600)

    return data

3. 本地缓存预热

def preload_hot_data():
    """预热热点数据到本地缓存"""

    # 获取热点新闻列表
    hot_news = get_hot_news_list()

    # 预加载到本地缓存
    for news in hot_news:
        cache_key = f'news:{news["id"]}'

        # 从 Redis 获取
        data = redis_client.get(cache_key)
        if data:
            # 存入本地缓存
            cache_manager.local_cache[cache_key] = data
            cache_manager.local_cache_ttl[cache_key] = time.time() + 60

# 定时预热:每 5 分钟
scheduler.add_job(
    preload_hot_data,
    'interval',
    minutes=5,
    id='preload_hot_data'
)

效果验证

优化前

热点新闻查询:
- 全部请求打到 Redis
- Redis 单 key QPS: 5000
- Redis CPU: 80%

优化后(多级缓存)

热点新闻查询:
- 80% 命中本地缓存(无网络开销)
- 15% 命中 Redis
- 5% 未命中,查询数据库

本地缓存命中率:80%
Redis QPS: 降到 1000
Redis CPU: 降到 30%

本节小结

✅ 完成的工作:

  • 实现了多级缓存
  • 实现了热点 key 分散
  • 实现了本地缓存预热

✅ 效果:

  • Redis 压力降低 80%
  • 响应时间进一步降低
  • 系统更稳定

⚠️ 下一步:活动期间流量暴增 10 倍

🎯 下一步:活动期间流量暴增,如何削峰?

搜索