后端缓存原理及常见问题
作为 server端经常会用到缓存技术来提供降低数据库压力和提高服务性能,因为一些数据我们没有必要每次查询的时候都去查询到数据库。今天读到一篇文章加深了自己对缓存的认识,整理一篇文档以作记录。
常见的缓存有redis等内存性缓存服务器。对于自己维护数据库而言,所有的请求都查询数据库会对数据库造成极大的压力。而对于正在使用的AWS dynamodb 这类以SaaS服务形式提供数据库就更加直接了,按次数收费或按可用QPS 最大值收费,性能直接就是财富。
1 缓存的基本实现
通常不用缓存的情况,每次业务请求,都需要去数据库查询。
使用缓存后,业务查询数据时,会先查询缓存,如果缓存中存在该数据则直接返回,如果没有则继续查询数据库,然后返回。并更新缓存。
原理上说起来是比较简单的情况。但实际使用上会出现比较多的异常情况需要考虑。比如下面的情况:
- 缓存穿透
- 缓存击穿
- 缓存雪崩
- 热点数据集中失效问题
2 缓存穿透
2.1 原理
正常情况下,需要业务系统查询的数据都是存在的。就会走我们上面图中的缓存流程。
但如果业务去请求数据库中不存在的数据,就会造成缓存和数据库都查询不到该数据,直接返回。这样每次查询这条数据都会造成缓存和数据库的查询并每次均会直接对数据库造成查询压力。这种查询找不存在数据的现象称为缓存穿透。
缓存穿透如果一旦被人恶意利用就会导致,利用一条不存在的id,产生大量的请求对数据库进行查询,这种情况很可能就会造成数据库压力过大引起宕机。(相当于缓存系统没有工作,而数据库性能是缓存系统工作情况下设计的)
2.2 解决方案
而缓存穿透一般情况下有两种解决方案:
1 缓存空值
发生穿透的原因是因为缓存中没有存储这些空数据的key,从而导致每次查询均会在缓存中拿不到值,每次都需要查询数据库。而我们可以将这些实际上不存在的key对应的值设置为null缓存到数据库中。以后在查询时,即可在缓存中查到值(null)并返回。这样就避开了每次对数据库的直接查询。这些缓存需要合理设置过期时间,防止对缓存服务器造成大大容量消耗。
2 BloomFilter
我们可以在缓存之前再加一道屏障,里面存储目前数据库中存在的所有key。BloomFilter 类似于一个hbase set 用来判断某个元素(key)是否存在于某个集合中。(PS:但想想这里面的key非常多的话,也要谨慎处理)实际行为如下图所示:
使用BloomFilter后,会存储数据库中所有key(不存值),查询时会先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回,存在再走查缓存 ,查 DB。这样就可以避免第一种缓存空值解决方案带来的缓存大量不存在的key对缓存服务器造成的压力。
对于恶意攻击的情况,空值key异常多、请求重复率比较低的数据,就没有必要进行缓存,使用第二种方案直接过滤掉。
而对于空数据的key有限的,重复率比较高的,则可以采用第一种方式进行缓存。
3 缓存击穿
在高并发情况下,大量请求查询同一个key时,而该key在缓存过期机制(有缓存则必须要有过期机制)的情况下正好失效了,就会导致大量请求发现缓存中该key不存在,直接向数据库发起查询请求导致数据库压力激增。
这种情况,我们可以在查询数据的请求上使用一个互斥锁,其他请求查询数据是发现拿不到锁,就进行等待,等待第一个线程查询到数据后,将数据缓存。后面的线程再从缓存中读取该数据。即缓存不存在时,发现要进行数据读时就应该对该数据的读取加锁了。
4 缓存雪崩
缓存雪崩是指,当某一时刻发生大规模的缓存失效的情况,如缓存服务宕机,这样就会有大量的请求直接查询数据库,导致数据库压力剧增,甚至引起宕机。
对于这种突发问题,首先从缓存设计上就要进行考虑,尽可能让缓存不挂。比如使用缓存集群,提供服务可用性。如redis的使用 主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的情况。
如果缓存挂掉了,我们应该采取一定方案,避免过多数据库查询请求直接导致数据库挂掉,导致整个系统异常。比如引入限流降级机制。比如使用防雪崩工具。 Hystrix是一款开源的“防雪崩工具”,它通过 熔断、降级、限流三个手段来降低雪崩发生后的损失。
Hystrix一旦发现当前服务的请求失败率达到预设的值,Hystrix将会拒绝随后该服务的所有请求,直接返回一个预设的结果。这就是所谓的“熔断”。当经过一段时间后,Hystrix会放行该服务的一部分请求,再次统计它的请求失败率。如果此时请求失败率符合预设值,则完全打开限流开关;如果请求失败率仍然很高,那么继续拒绝该服务的所有请求。这就是所谓的“限流”。而Hystrix向那些被拒绝的请求直接返回一个预设结果,被称为“降级”。
Hystrix 介绍:
https://segmentfault.com/a/1190000005988895
在维持住服务后,我们应该尽快恢复缓存数据并提供服务。使用Redis时应该开启缓存持久化,重启后能够从磁盘加载数据从而恢复缓存功能。
5 热点数据集中失效问题
有缓存就必须要有过期机制和管理机制。一般而言。在设置缓存是,我们都会设置一个过期时间。而对于某些热点数据。一点缓存失效就会存在大量请求,导致数据库压力激增。
而这种情况,我们对于过期机制而言,应该为这些热点数据过期时间添加随机值,使它们的过期时间错开,防止造成较多的热点数据过期,造成数据库查询峰值。第二个方面,对于热点数据过期造成的问题也可以通过3中加读写锁的方式进行处理,但这样会阻塞住其他线程造成系统吞吐率下降。所以对于热点数据的过期机制一定要谨慎设计。
6 参考资料
https://blog.****.net/u010425776/article/details/79555894
https://mp.weixin.qq.com/s/7gbJCeBKklTlAxU_vsrIxg