缓存雪崩
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决:
-
设置随机过期时间:对于热点数据,可以不设置过期时间,或者为缓存数据设置随机的过期时间,以避免大量数据同时过期,减少对数据库的压力。例如,可以将基础过期时间加上一个随机值来设计缓存的过期时间,这样数据会在未来逐渐过期,避免同时过期造成数据库压力过大。
-
预热缓存:提前将热点数据加载到Redis中,并设置一个较长的过期时间,这样可以减少缓存失效的发生。
-
使用分布式锁:当检测到缓存失效时,不是立即从数据库加载数据,而是先尝试获取分布式锁。成功获取锁后,再执行数据库查询和缓存数据写入操作。如果获取锁失败,则表示当前有其他线程正在执行数据库查询操作,当前线程可以稍作休眠后重试。这样可以确保只有一个请求去数据库读取数据。
-
缓存空值:当请求的数据在Redis和数据库中都不存在时,可以设置一个默认值(例如:None)。这样,当后续再次查询时,直接返回空值或默认值,避免再次访问数据库。
-
使用布隆过滤器:在数据写入数据库时,将ID同步到布隆过滤器中。当请求的ID在布隆过滤器中不存在时,意味着请求查询的数据一定不在数据库中,因此不需要去数据库查询。布隆过滤器需要缓存全部的键,因此适合数据量不大的情况,例如少于10亿条数据,因为10亿条数据大约会占用1.2GB的内存。
-
接口限流:对于非核心数据的访问,可以在查询方法上添加接口限流保护,例如设置每秒10000个请求。如果核心数据接口被访问,缓存不存在,则允许从数据库查询并设置到缓存。这样,只有部分请求会被发送到数据库,减轻压力。
-
构建高可用缓存集群系统:为了防止缓存系统故障导致的缓存雪崩,必须构建Redis高可用集群,如"Redis Sentinel Cluster"或"Redis Cluster Cluster",如果Redis的主节点失败,从节点也可以切换成为主节点,继续提供缓存服务,避免因缓存实例宕机导致的缓存雪崩。
缓存击穿
是指一个系统中的缓存数据在被请求时恰好失效,而此时如果有大量并发请求同时到达,这些请求都会穿透缓存,直接打到后端数据库上。这种情况可能会导致数据库瞬时压力过大,甚至可能因为无法承受突如其来的高并发流量而崩溃。 (与缓存雪崩不同的是击穿指一个数据而雪崩是多个数据)
解决
-
加锁策略:在缓存失效时,使用分布式锁来确保只有一个请求能够访问数据库并重新加载缓存,其他请求则等待或重试。
-
缓存空对象:如果数据库查询结果为空,仍然将这个空结果缓存起来,这样即使缓存失效,请求也会从缓存中快速失败,而不是每次都去数据库查询。
-
设置合理的过期时间:根据数据的访问频率和更新频率来设置缓存的过期时间,避免缓存在同一时间大量过期。
-
使用随机过期时间:为缓存设置一个随机的过期时间,这样即使多个请求同时到达,也不大可能同时击穿缓存。
-
读写分离:通过数据库的读写分离,将缓存击穿的流量分散到多个数据库副本上,减轻单个数据库的压力。
-
限流和降级:在系统检测到缓存击穿时,启动限流措施,减少对数据库的访问压力,同时可以启动降级策略,提供备选的服务方案。
缓存穿透
是指请求查询数据库中不存在的数据,由于在缓存中也没有这些数据的记录,因此请求会直接穿透缓存,每次都直接访问数据库。
解决
-
缓存空值:当数据库查询结果为空时,仍然将这个空结果缓存起来,并设置一个较短的过期时间。这样,下次同样的请求会直接命中缓存,而不会去查询数据库。
-
布隆过滤器(Bloom Filter):在缓存之前使用布隆过滤器来检查一个请求是否可能命中。布隆过滤器可以快速判断一个元素是否在一个集合中,从而避免对数据库的无效查询。
-
预加载缓存:对于可能被频繁查询的数据,可以提前将它们加载到缓存中,即使这些数据在数据库中暂时不存在,也可以在缓存中设置一个默认的空值响应。
-
限制频繁请求:通过限流措施来限制对同一数据的频繁访问,减少对数据库的压力。
-
使用互斥锁:当检测到缓存未命中时,使用互斥锁来确保只有一个线程去查询数据库,并将结果缓存起来,其他线程则等待或重试。
-
数据库查询结果缓存:即使查询结果为空,也将其缓存起来,并设置一个合理的过期时间,这样在过期时间内相同的查询将直接返回缓存结果。