
在快手推荐系统中,通过Redis缓存用户画像(Hash结构存储用户兴趣标签)和热点内容(ZSet存储热度分数),结合布隆过滤器防缓存穿透、随机TTL防缓存雪崩、分布式锁防缓存击穿等策略,有效提升系统响应速度,降低数据库压力,并缓解缓存风险。
老师口吻:Redis作为缓存的核心优势是高并发读写能力,能快速响应推荐请求。具体应用场景包括:
缓存策略设计:
SETNX命令加锁,超时10秒),确保只有一个线程更新缓存,其他线程等待。| 数据结构 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Hash | 键值对集合,键为字符串,值为哈希 | 支持快速读写,适合存储结构化数据 | 用户画像(兴趣标签、行为数据) | 避免频繁更新导致缓存失效,需合理设置TTL |
| ZSet | 有序集合,元素带分数(score) | 支持按分数排序,范围查询 | 热点视频列表(按热度排序Top N) | 分数更新频繁,需考虑并发控制(如分布式锁) |
| List | 链表结构,支持左/右插入 | 顺序存储,适合队列、列表 | 用户行为日志(点击、点赞列表) | 长列表可能影响性能,需定期清理 |
用户行为数据缓存(伪代码):
public void updateUserBehavior(String userId, String videoId) {
db.updateBehavior(userId, videoId); // 更新数据库
redis.hset("user:behavior:" + userId, videoId, "1"); // 更新Redis哈希
redis.expire("user:behavior:" + userId, 300); // 5分钟TTL
}
热点视频列表更新(伪代码):
public void updateHotVideos(List<Video> videos) {
for (Video v : videos) {
redis.zadd("hot_videos", v.getScore() + 1, v.getId()); // 更新ZSet分数
redis.expire("hot_videos", 3600); // 1小时TTL
}
}
“面试官您好,在快手推荐系统中,我们主要利用Redis缓存用户画像和热点内容来提升系统性能。用户画像缓存用Hash结构存储用户兴趣标签,比如用户ID为key,value是标签列表(如电影、音乐),缓存TTL设为5分钟,因为用户行为会变化。当用户请求推荐时,先查Redis,若不存在再查数据库,减少数据库压力。热点内容用有序集合ZSet,存储视频ID和热度分数,通过score排序获取Top 10热门视频,缓存TTL设为1小时。缓存策略方面,缓存穿透用布隆过滤器,比如查询不存在的视频时,先通过布隆过滤器判断,若不存在则直接返回,避免数据库全量查询;缓存雪崩用随机TTL,每个视频的过期时间随机偏移(±10%),避免集中过期;缓存击穿用分布式锁,比如更新热点视频列表时,只有一个线程获取锁并更新缓存,其他线程等待,防止并发更新导致数据不一致。这样能高效缓存数据,提升推荐速度。”
问:布隆过滤器的误判率如何?具体参数怎么设置?
回答要点:布隆过滤器是概率性数据结构,误判率约1%,通过位数组大小(如2^16=65536位)和哈希函数数量(k=3)控制,误判率公式为 (1 - e^{-kn/p}),其中p是位数组填充率(如0.5时,误判率约1%)。
问:缓存击穿时,分布式锁的锁超时时间如何设置?
回答要点:锁超时设为10秒,避免锁被占用过久,影响其他线程的请求处理,同时确保锁在异常情况下能自动释放。
问:用户画像数据量很大时,如何优化?
回答要点:对用户ID进行哈希分片(如MD5哈希取模),每个分片对应一个Redis实例,减少单实例压力,同时保证数据分布均匀。
问:热点数据更新时,如何保证数据一致性?
回答要点:采用乐观锁(检查当前分数是否被其他线程修改)或分布式锁(保证更新原子性),确保热点视频分数的更新一致性。