1.开篇
学习一门技术,目的肯定是为了在业务中使用它。那么,Redis到底能干些什么?最常用的当属拿Redis做缓存。今天就来学习Redis的作用之一--做缓存。学完此篇,你将掌握:
- Redis做缓存的工作原理;
- 缓存的替换策略;
- 缓存异常的原因以及应对方法;
- 缓存与数据库一致性保证的方法;
1.1 Redis缓存的工作原理
- 缓存的本质:
- 使用更快的介质/系统加速慢速介质/系统的访问
- 快速介质/系统的容量通常更小
- 缓存类型
- 只读缓存,redis做只读缓存多一些;
- 读写缓存
- 缓存的应用场景:加速数据库的访问
- 缓存的请求处理流程:
- 首先查询缓存,如果缓存中有请求的数据,则缓存命中,从缓存中读取数据;相反,如果缓存中没有请求的数据,则缓存缺失,从后端数据库当中读取数据,此外还要做一个操作--把这个缺失的数据写入缓存。
- 缓存满了怎么办?缓存容量毕竟是有限的。如果缓存存满数据后,再有新数据要写入缓存,此时会有相关的操作:
- 哪些数据将从缓存中替换出,即一个进,对应一个出。这叫替换策略;
- 替换出的数据如何处理?这涉及到数据如何写回。这叫写回策略,只在读写缓存中存在数据写回这个操作。有两种:
- Write-through策略:写缓存的同时写数据库,优点是可靠性高,有可能快
- Write-back策略:先写缓存后写数据库,优先是性能快
1.2 Redis缓存的替换策略,也叫淘汰策略
- 按照候选数据集区分:
- volatile-xxx;具体来说,就是给某些数据设置过期时间,淘汰数据时,从已设置过期时间的key中淘汰数据,没设置的数据将永久驻留。热点数据经常使用这个策略;
- allkeys-xxx。缓存的全部数据都可淘汰。
- 按策略区分:ttl,random,LRU(Least Recently Used),LFU(Least Frequently Used)
1.3 Redis缓存异常
实战问题1:缓存中大量热数据同时失效会有什么问题么?关键词:大量数据,同时;导致:缓存雪崩
- 场景:Redis作为MySQL的前端缓存,针对同一时刻加载到Redis中的数据,采用了volatile-ttl淘汰策略,并设置相同的过期时间,到期后同时失效。
- 现象:MySQL的QPS一下飙到6k OPS(正常是3k OPS),数据库系统资源占用急剧上升,系统报警。
- 分析:
- Redis中大量数据同时失效,导致发生大量缓存缺失。数据请求转发到数据库,导致DB压力剧增。导致缓存雪崩现象;
- 缺失的数据再更新到Redis,导致Redis压力增加。
- 应对方法:
- 给数据设置不同TTL值;
- Redis实例异常,也会导致雪崩发生,通过主从集群增加缓存可靠性。
实战问题2:缓存中某一热点数据失效会有什么问题么?导致:缓存击穿
- 场景:Redis作为MySQL的前端缓存,某一热点数据过期失效。
- 现象:MySQL数据库QPS增加,数据库系统资源占用增加。
- 分析:不同于雪崩,仅少量热点数据失效;缓存击穿现象。
- 应对方法:热点数据不设置过期时间。
实战问题3:数据库误删数据会对缓存有影响么?导致:缓存穿透
- 场景:Redis作为MySQL的前端缓存,采用volatile-ttl替换策略。业务部门误删了某些数据。
- 现象:过段时间后,Redis的QPS突然增加,MySQL数据库的QPS也相应增加。
- 分析:
- 被误删除的数据再Redis的过期时间到达后,数据失效,被淘汰出缓存了;
- 有请求需要读取这部分数据,在Redis和MySQL都找不到这些数据;
- 如果并发请求很多,那么将导致Redis和MySQL的压力都会增加;
- 这叫缓存穿透现象。
- 应对方法:
- 缓存缺失后,DB中查询不到的数据,在Redis中设置为NULL;
- 使用布隆过滤器(Bloom Filter),让Redis快速返回;
- Redis和MySQL压力超过报警阈值,触发前端限流。
思考题:前端限流对缓存雪崩,击穿,穿透都有用么?如果是恶意访问不存在的key,前端限流是个好方案吗?
- 参考回答:
- 前端下流作为一个成熟的系统级方案,一般建议进行部署;
- 雪崩,击穿,穿透都会对数据库造成压力,前端限流会有帮助;
- 前端限流是一个有损方案,对于恶意访问,建议在网关处进行请求校验。
1.4 缓存数据一致性如何保证?
-
场景:Redis作为MySQL的前端缓存,业务应用需要对某一数据进行更新。
-
问题:现有两种设计方案,该如何选择?我们上面说过,Redis做缓存更多时候做的是只读的,这里就不考虑更新缓存了,只考虑删除缓存。
- 方案一:先删缓存,再更新数据库;
- 方案二:先更新数据库,再删除缓存。
-
方案一分析:
- 先删除缓存,数据库尚未更新->此时有并发请求访问缓存—>缓存已删除,导致缓存缺失->从数据库读取旧值
- 先删除缓存,更新数据库失败->重试更新数据库还是失败->有并发请求访问缓存->缓存缺失->从数据库读取旧值
- 如果重试更新数据库成功了,是否就没问题了呢?
- 分析:先删除缓存,更新数据库失败->重试更新数据库成功->有并发请求访问缓存->缓存缺失->数据库已经更新,从数据库读取新值
-
方案二分析:
- 先更新数据库,删除缓存失败->此时有并发请求访问缓存->缓存还没成功删除,缓存命中->从缓存读取旧值
- 如果删除缓存这个操作也是可以重试的,那么在删除了缓存后,通过缓存缺失,读取到新值,这时两个方案没什么差别。
-
如何选择方案,还是具体情况具体分析。
总结:
- Redis缓存以只读缓存应用为主,常用的缓存淘汰策略包括基于过期时间、LRU、LFU;
- Redis缓存异常包括雪崩,击穿,穿透。雪崩和击穿都是指有数据在缓存中缺失,压力传导至DB。而穿透则是指缓存和DB都要承受大压力;
- Redis缓存和数据库一致性保证时,先删缓存再更新数据库,或者先更新数据库再删除缓存是两种不同的方案。
- 今天的学习就先到这里,《蒋德钧的Redis集训班》Day three