redis系列-缓存雪崩和缓存穿透问题,简单redis的缓存雪崩和缓存穿透问题,及相应的解决方案。
缓存雪崩
简介
缓存雪崩是指在同一时间,redis缓存大面积失效,后面的请求都会落在数据库上,造成数据库短时间内承受大量的请求而崩掉。
解决办法
- 部署redis集群,尽量保证集群的高可用性,发现机器宕机尽快补上,选择合适的内存淘汰策略。
- 本地使用ehcache缓存+hystrix限流降级 ,避免高并发使mysql数据库崩掉。
- 做好redis持久化,发生缓存雪崩后,利用redis持久化保存的数据,尽快恢复缓存。
使用本地ehcache缓存+hystrix限流降级请求访问过程:系统收到请求后,先查询本地ehcache缓存,如果没查到在查redis。如果ehcache和redis都没有,在查数据库,将数据库中的结果,写入ehcache和redis中。这样做的好处:
- 数据库不会直接崩掉,限流组件确保了每秒只有多少请求能通过,剩余的等待。
- 只要数据库不死,对用户来说,五分之二的请求都可以被处理的。
- 对于用户来说,只是等待的时间长了一点,确保了数据不会丢失。
缓存穿透
简介
缓存穿透说简单点就是大量请求的key不存在于缓存中,导致请求直接落到了数据库上,根本没有经过缓存这一层。大量的并发查询数据库,就会直接让数据库崩掉。
正常的缓存处理:
一般Mysql默认最大连接数在150左右,可以通过命令 show variables like ‘%max_connections%’; 查看。cpu、内存、磁盘、网络都会限制并发能力,一般3000个并发请求就能打死大部分数据库了。
解决办法
缓存无效 key:如果缓存和数据库都查不到,就缓存一个不存在的key,并设置过期时间,命令为:
1
SET key value EX 10086
这种方式可以解决请求的key变化不频繁的情况,如果每次构建不同的key,会导致redis缓存大量的无效key,使用这种方式的话,尽量将无效的key过期的时间设置尽量小一点。
java代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public Object getObjectInclNullById(Integer id) {
// 从缓存中获取数据
Object cacheValue = cache.get(id);
// 缓存为空
if (cacheValue == null) {
// 从数据库中获取
Object storageValue = storage.get(key);
// 缓存空对象
cache.set(key, storageValue);
// 如果存储数据为空,需要设置一个过期时间(300秒)
if (storageValue == null) {
// 必须设置过期时间,否则有被攻击的风险
cache.expire(key, 60 * 5);
}
return storageValue;
}
return cacheValue;
}布隆过滤器:布隆过滤器是由二进制向量(或者数组)和一系列的映射哈希函数 两部分组成的数据结构。把所有存在的请求的值都存放在布隆过滤器中,当请求过来,先判断请求是否在布隆过滤器中,如果不存在直接返回错误提示,存在才会继续走下面流程。
缓存击穿
简介
缓存击穿就是某个key非常热点,访问非常频繁,处于集中高并发访问,当这个key在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决办法
- 如果缓存的数据基本不会发生更新,尽量不要将热点数据设置过期时间
- 如果数据更新不频繁,可以采用基于redis、zookeeper等分布式中间件的分布式互斥锁,或者本地互斥锁以保证大量并发访问时,少部分可以访问,其余请求等待锁释放后访问。
- 如果数据更新频繁,可以利于定时线程在缓存过期前重新构建缓存或者延后过期时间。