缓存穿透
现象
正常情况下,我们去查询数据都是存在。那么请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去。这种查询不存在数据的现象我们称为缓存穿透。试想一下,如果有黑客会对你的系统进行攻击,拿一个不存在的id去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉。
方案
- 缓存空值:之所以会发生穿透,就是因为缓存中没有存储这些空数据的key。从而导致每次查询都到数据库去了。那么我们就可以为这些key对应的值设置为null 丢到缓存里面去。后面再出现查询这个key的请求的时候,直接返回null。这样,就不用在到数据库中去走一圈了,但是别忘了设置过期时间。
- 布隆过滤器:BloomFilter 类似于一个hbase set用来判断某个元素(key)是否存在于某个集合中。这种方式在大数据场景应用比较多,比如Hbase中使用它去判断数据是否在磁盘上。还有在爬虫场景判断url是否已经被爬取过。这种方案可以加在第一种方案中,在缓存之前在加一层BloomFilter,在查询的时候先 BloomFilter 去查询key是否存在,如果不存在就直接返回,存在再走查缓存->查DB。
针对于一些恶意攻击,攻击带过来的大量key是不存在的,那么我们采用第一种方案就会缓存大量不存在key的数据。此时我们采用第一种方案就不合适了,我们完全可以先对使用第二种方案进行过滤掉这些key。针对这种key异常多、请求重复率比较低的数据,我们就没有必要进行缓存,使用第二种方案直接过滤掉。而对于空数据的key有限的,重复率比较高的,我们则可以采用第一种方式进行缓存。
缓存击穿
现象
缓存击穿是我们可能遇到的第二个使用缓存方案可能遇到的问题。在平常高并发的系统中,大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。会造成某一时刻数据库请求量过大,压力剧增。
方案
- 对热点key,加互斥锁
- 设置热点数据永不过期
- 资源保护,服务降级
缓存雪崩
现象
缓存雪崩,是指缓存中设置了大批量相同过期时间的数据同时过期失效,而在这一刻访问量剧增,缓存近乎失效,所有请求全部转向DB,DB瞬时压力过重雪崩,甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
方案
事前:使用集群缓存,保证缓存服务的高可用。这种方案就是在发生雪崩前对缓存集群实现高可用,如果是使用Redis,可以使用主从+哨兵,Redis Cluster 来避免Redis 全盘崩溃的情况。过期时间设置随机,防止同一时间大量数据过期现象发生,为了避免这些热点的数据集中失效,那么我们在设置缓存过期时间的时候,我们让他们失效的时间错开。比如在一个基础的时间上加上或者减去一个范围内的随机值。
事中:ehcache本地缓存+Hystrix限流&降级,避免MySQL被打死使用ehcache本地缓存的目的也是考虑在Redis Cluster 完全不可用的时候,ehcache本地缓存还能够支撑一阵。使用Hystrix进行限流&降级,比如一秒来了5000个请求,我们可以设置假设只能有一秒2000个请求能通过这个组件,那么其他剩余的3000请求就会然后去调用我们自己开发的降级组件(降级),比如设置的一些默认值呀之类的。以此来保护最后 MySQL不会被大量的请求给打死。
事后:开启Redis持久化机制,尽快恢复缓存集群一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。