
采用“CDN+本地缓存+Redis”多级缓存架构,结合随机TTL、缓存预热、分布式锁防雪崩,通过阶梯式失效策略提升高频数据响应速度并有效避免缓存雪崩。
首先解释缓存雪崩:高频数据若缓存策略不当,会因大量缓存同时过期,导致请求集中到数据库(类比“超市所有热门商品突然卖完,顾客都涌向仓库取货,仓库瞬间拥堵”)。解决核心是:
缓存预热:系统启动或低峰期(如凌晨),预加载热门数据(如热门课程、高频习题)到缓存,减少首次访问延迟(类比“超市开业前把热门商品提前摆好,顾客进门就能看到,无需等待”)。
缓存击穿:热点数据突然失效(如课程被删除),大量请求直接到数据库。解决方法:设置默认值(如“数据加载中”)或用互斥锁(仅第一个请求去数据库,后续返回默认值)。
| 策略/工具 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| TTL(随机偏移) | 为缓存数据设置带随机偏移的过期时间(如1小时+5分钟随机) | 数据过期后自动失效,系统定时清理 | 热点数据,需避免集中过期 | 随机偏移需合理(避免偏移过小仍集中过期) |
| 本地缓存 | 客户端(浏览器)或应用层缓存 | 数据常驻,减少请求次数 | 热点数据(如课程列表、热门习题),用户频繁访问 | 需考虑更新一致性(如本地缓存过期时间设置) |
| 分布式锁(如Redis SETNX) | 控制并发写入的锁机制 | 仅获取锁的实例可更新缓存,其他等待 | 缓存更新时,防并发导致雪崩 | 锁超时需设置合理(避免死锁,如10秒) |
| 缓存预热 | 系统启动/低峰期预加载数据到缓存 | 减少首次访问延迟 | 热门数据(如每日课程、高频习题) | 预加载数据范围需覆盖高频访问,TTL设置较长(如12小时) |
以“课程列表”获取流程为例(伪代码,展示多级缓存与分布式锁):
function getCourseList() {
// 1. 检查本地缓存(浏览器/应用层)
const localCache = getLocalCache('course_list');
if (localCache) return localCache;
// 2. 检查Redis缓存(随机TTL)
const redisKey = `course_list:${currentWeek}`;
const redisData = redis.get(redisKey);
if (redisData) return parseCourseList(redisData);
// 3. 从数据库获取
const dbData = queryDatabase('SELECT * FROM courses WHERE week = ?', [currentWeek]);
if (!dbData) return []; // 无数据时返回空
// 4. 更新缓存(分布式锁防雪崩)
const lockKey = `course_list_lock:${currentWeek}`;
const lockAcquired = redis.set(lockKey, '1', 'EX', 10); // 10秒锁,防超时
if (lockAcquired) {
// 设置随机TTL(1小时+5分钟偏移)
const ttl = 3600 + Math.floor(Math.random() * 300); // 1小时+0~5分钟
redis.set(redisKey, JSON.stringify(dbData), 'EX', ttl);
setLocalCache('course_list', dbData); // 更新本地缓存
redis.del(lockKey); // 释放锁
return dbData;
} else {
// 锁未获取,返回旧数据或等待
return redis.get(redisKey) ? parseCourseList(redis.get(redisKey)) : [];
}
}
(注:CDN可缓存静态资源,如课程图片、页面静态文件,减少应用层压力。)
面试官您好,针对课程列表、习题库等高频访问数据,我会设计一个多级缓存架构。首先,采用本地缓存(如浏览器或应用层)存储热点数据,减少请求次数;中间层用Redis作为共享缓存,为每个数据设置随机TTL(比如1小时+5分钟随机偏移),避免集中过期;同时,系统启动时通过缓存预热预加载热门数据,减少首次访问延迟。对于缓存更新,使用分布式锁(如Redis的SETNX命令)控制并发,防止多个实例同时写入导致雪崩。具体来说,比如课程列表,先检查本地缓存,若没有则从Redis获取,若Redis也没有则从数据库,更新后设置1小时TTL并随机偏移,同时更新本地缓存。这样既能提升响应速度,又能有效避免缓存雪崩。
问:如何具体实现缓存预热?比如预加载哪些数据?
回答要点:在系统启动或低峰期(如凌晨0-2点),通过后台任务预加载热门课程(如上周热门课程)、高频习题库(如数学题库)等数据到Redis,设置较长的TTL(如12小时),减少首次访问延迟。
问:如果Redis主库宕机,如何保证服务可用性?
回答要点:配置Redis主从复制,主库故障时自动切换从库,同时本地缓存作为临时备份,减少对数据库的压力,保证服务可用性。
问:缓存击穿(如热门课程被删除)时,设置默认值是否合理?
回答要点:对于热点数据,设置默认值(如“数据加载中”)是可行的,能减少数据库压力;若需精确数据,可用互斥锁,但需设置锁超时(如5秒),避免第一个请求延迟过长。
问:本地缓存与Redis的更新不一致怎么办?
回答要点:采用“写回”策略,数据库更新时同时更新Redis和本地缓存;或本地缓存设置较短的过期时间(如5分钟),确保数据一致性。
问:随机TTL在分布式环境下仍可能集中过期吗?如何缓解?
回答要点:是的,多个实例的随机偏移可能重叠,导致集中过期。缓解方法:结合版本号或时间戳的随机偏移(如TTL=1小时+时间戳偏移),减少重叠概率。