MST

星途 面试题库

面试题:Redis设置键存活时长在缓存场景中的应用优化

假设在一个高并发的缓存场景中,使用Redis设置键存活时长来管理缓存数据。如何避免大量键同时过期导致的缓存雪崩问题?请给出至少两种策略并详细说明实现思路。
15.1万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 分散过期时间
    • 实现思路:在设置键的过期时间时,不采用固定的时长,而是在一个基础时长上,增加一个随机的时间偏移量。例如,原本计划设置缓存过期时间为60分钟,可以改为设置为55 - 65分钟之间的随机值。这样,大量缓存键就不会在同一时刻过期,从而降低缓存雪崩的风险。在代码实现上,如果使用Python的redis - py库,可以类似这样设置:
import redis
import random

r = redis.Redis(host='localhost', port=6379, db = 0)
base_expire_time = 60 * 60  # 基础过期时间,单位秒
random_offset = random.randint(0, 600)  # 随机偏移量,0 - 600秒
total_expire_time = base_expire_time + random_offset
r.setex('key', total_expire_time, 'value')
  1. 使用二级缓存
    • 实现思路:除了Redis主缓存外,再设置一层本地缓存(如Python中的functools.lru_cache,或Guava Cache等)作为二级缓存。当请求到达时,首先检查本地缓存中是否有数据,如果有则直接返回;若没有,再去查询Redis缓存。如果Redis中缓存过期,此时本地缓存仍可能有数据,可继续提供服务,避免大量请求直接穿透到后端数据库。同时,在后台启动一个异步任务,负责在Redis缓存过期后,重新从数据库加载数据更新到Redis和本地缓存中。以Python为例,结合functools.lru_cache实现如下:
import redis
import functools

r = redis.Redis(host='localhost', port=6379, db = 0)

@functools.lru_cache(maxsize = 128)
def get_data_from_cache(key):
    data = r.get(key)
    if data:
        return data.decode('utf - 8')
    else:
        # 从数据库获取数据,这里假设为get_data_from_db函数
        data = get_data_from_db(key)
        r.setex(key, 3600, data)  # 设置Redis缓存,过期时间1小时
        return data


def update_cache_async(key):
    # 异步任务,重新从数据库加载数据更新缓存
    data = get_data_from_db(key)
    r.setex(key, 3600, data)
    get_data_from_cache.cache_clear()  # 清除本地缓存,以便下次重新加载
  1. 永不过期策略
    • 实现思路:对于一些重要且不常变化的数据,设置键永不过期。同时,使用一个后台线程或定时任务,定期检查数据的有效性,若数据已过时,则更新Redis中的数据。例如,通过Python的threading.Threadtime.sleep实现定时更新:
import redis
import threading
import time


r = redis.Redis(host='localhost', port=6379, db = 0)


def update_data_periodically():
    while True:
        key = 'important_key'
        # 检查数据是否过时,假设is_data_outdated函数判断数据是否过时
        if is_data_outdated(r.get(key)):
            new_data = get_new_data()
            r.set(key, new_data)
        time.sleep(3600)  # 每小时检查一次


t = threading.Thread(target = update_data_periodically)
t.start()
  1. 缓存预热
    • 实现思路:在系统上线前或系统初始化阶段,预先将部分热点数据加载到Redis缓存中,并设置合理的过期时间。由于这些数据在系统启动时就已经存在于缓存中,并且其过期时间是分散设置的,所以在系统运行初期不会因为大量键同时过期而引发缓存雪崩。可以编写一个初始化脚本,批量从数据库读取热点数据并设置到Redis中,例如:
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)
hot_keys = get_hot_keys_from_db()  # 获取热点键列表
for key in hot_keys:
    data = get_data_from_db(key)
    base_expire_time = 3600
    random_offset = random.randint(0, 600)
    total_expire_time = base_expire_time + random_offset
    r.setex(key, total_expire_time, data)