MST

星途 面试题库

面试题:Redis与MySQL缓存结合时,如何处理缓存雪崩问题?

在使用Redis与MySQL结合应对高并发写请求场景中,缓存雪崩是一个常见问题。请阐述缓存雪崩产生的原因,并说明至少两种应对缓存雪崩的方法。
11.9万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

缓存雪崩产生的原因

  1. 大量缓存同时过期:在高并发系统中,如果大量的缓存数据设置了相同或相近的过期时间,当这些缓存同时过期时,大量原本由缓存处理的请求会瞬间直接涌向数据库,导致数据库负载急剧增加,甚至可能因不堪重负而崩溃。
  2. 缓存服务故障:Redis 作为缓存服务,如果因为某些原因(如服务器宕机、网络故障等)出现不可用情况,所有依赖缓存的请求都会转而访问数据库,引发数据库压力剧增,造成雪崩效应。

应对缓存雪崩的方法

  1. 设置随机过期时间:在设置缓存过期时间时,为每个缓存数据添加一个随机的过期时间偏移量,避免大量缓存同时过期。例如,原本设置过期时间为 60 分钟,可以改为 55 - 65 分钟之间的随机值。这样能分散缓存过期时间,减轻数据库压力。
import random
import redis

r = redis.Redis(host='localhost', port=6379, db=0)
expire_time = 60 * 60 + random.randint(-300, 300)
r.setex('key', expire_time, 'value')
  1. 使用互斥锁:当缓存失效时,先使用互斥锁(如 Redis 的 SETNX 命令)来保证只有一个请求能去查询数据库并更新缓存,其他请求等待。这样可以防止大量请求同时查询数据库,避免数据库压力过大。
import redis

r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = 'lock_key'
while True:
    if r.setnx(lock_key, 1):
        try:
            # 查询数据库
            data = get_data_from_db()
            # 更新缓存
            r.set('key', data)
        finally:
            r.delete(lock_key)
        break
    else:
        # 等待一段时间后重试
        time.sleep(0.1)
  1. 二级缓存方案:采用二级缓存结构,例如一级缓存使用 Redis,二级缓存可以使用本地缓存(如 Python 的 functools.lru_cache)。当一级缓存失效时,先从二级缓存获取数据,如果二级缓存也没有,再查询数据库。这样能在一定程度上减轻数据库压力。
import functools

@functools.lru_cache(maxsize=128)
def get_data():
    data = r.get('key')
    if data is None:
        data = get_data_from_db()
        r.set('key', data)
    return data
  1. 缓存预热:在系统上线前,提前将一些热点数据加载到缓存中,并设置合理的过期时间。这样可以避免系统刚上线时大量请求直接冲击数据库,同时也减少了缓存同时过期的可能性。
hot_keys = ['key1', 'key2', 'key3']
for key in hot_keys:
    data = get_data_from_db(key)
    r.set(key, data)
  1. 服务熔断与降级:当检测到数据库压力过大时,通过服务熔断机制暂时切断部分请求对数据库的访问,并返回兜底数据(如默认数据或缓存中旧数据),避免数据库被压垮。同时,开启服务降级,关闭一些非核心业务,保证核心业务的正常运行。例如,在 Python 中可以使用 circuitbreaker 库实现服务熔断。
from circuitbreaker import circuit

@circuit(failure_threshold=5, recovery_timeout=60)
def get_data_from_db():
    # 数据库查询逻辑
    pass