- 分散过期时间
- 实现思路:在设置键的过期时间时,不采用固定的时长,而是在一个基础时长上,增加一个随机的时间偏移量。例如,原本计划设置缓存过期时间为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')
- 使用二级缓存
- 实现思路:除了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() # 清除本地缓存,以便下次重新加载
- 永不过期策略
- 实现思路:对于一些重要且不常变化的数据,设置键永不过期。同时,使用一个后台线程或定时任务,定期检查数据的有效性,若数据已过时,则更新Redis中的数据。例如,通过Python的
threading.Thread
和time.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()
- 缓存预热
- 实现思路:在系统上线前或系统初始化阶段,预先将部分热点数据加载到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)