缓存击穿
缓存击穿(Cache Breakdown): 当某个缓存键的缓存失效时(如,过期时间),同时有大量的请求到达,并且这些请求都需要获取相同的数据,这些请求会同时绕过缓存系统,直接访问数据库。由于在缓存失效的瞬间,数据库可能会承受大量的请求压力,导致响应时间增加,甚至可能导致系统崩溃。
造成缓存击穿的原因可能包括:
- 热点数据:某些特定的数据在短时间内被大量请求访问,导致缓存失效。
- 缓存失效策略不当:缓存系统的失效策略可能不够灵活或者不合理,导致某些数据的缓存过期时间设置过短,容易触发缓存击穿。
- 并发请求过多:系统同时接收到大量的并发请求,导致缓存失效的同时大量的请求同时访问相同的数据。
为了避免缓存击穿的问题,可以采取以下策略:
- 使用互斥锁:可以使用互斥锁或者分布式锁等机制,确保只有一个请求能够查询数据库,其他请求等待查询结果返回。
- 逻辑过期:在缓存数据失效之前,通过一些逻辑手段来判断是否需要更新缓存数据,从而避免大量请求同时访问数据库。
方案一——互斥锁:
流程:
-
请求到达时的缓存查询:
- 当有请求到达时,首先检查缓存中是否存在对应的数据。
- 如果缓存中存在数据,直接返回数据给客户端。
- 如果缓存中不存在数据,则进入下一步处理。
-
获取互斥锁:
- 当缓存中不存在数据时,先尝试获取一个互斥锁。
- 如果成功获取到互斥锁,则继续下一步处理;如果获取失败(即有其他请求已经获取了锁),则等待一段时间后重试或者直接返回错误信息给客户端。
-
查询数据库:
- 获取到互斥锁后,再次检查缓存中是否存在数据,因为在等待获取锁的过程中,可能有其他请求已经查询到了数据并放入缓存中。
- 如果缓存中存在数据,释放互斥锁,然后直接返回数据给客户端。
- 如果缓存中仍然不存在数据,则查询数据库获取数据。
-
更新缓存:
- 获取到数据后,将数据存入缓存中。
- 设置合适的缓存过期时间。
- 释放互斥锁。
-
返回数据:
- 将查询到的数据返回给客户端。
过程如图:
特点: 强一致,性能差。
方案二——逻辑过期
逻辑过期是指在缓存数据失效之前,通过一些逻辑手段来判断是否需要更新缓存数据,从而避免大量请求同时访问数据库。
实现步骤:
-
设置逻辑过期时间:为缓存数据设置两个过期时间,一个是实际的物理过期时间,另一个是逻辑过期时间。逻辑过期时间比物理过期时间要早一些,通常在物理过期时间的一小段时间内。
-
请求到达时的逻辑判断:当有请求到达时,首先查询缓存数据是否过期(是否到达逻辑过期时间)。如果缓存数据已经过期,进入下一步逻辑判断;如果缓存数据未过期,则直接返回缓存数据。
-
逻辑判断:在缓存数据过期后,不立即去底层存储系统查询新数据,而是通过一些逻辑手段来判断是否需要更新缓存数据。例如,可以使用分布式锁来确保只有一个线程能够去查询底层存储系统,其他线程等待查询结果返回;或者可以先返回旧数据,并且在后台异步更新缓存数据。
-
更新缓存数据:如果经过逻辑判断确定需要更新缓存数据,那么就去底层存储系统查询新数据,并将新数据存入缓存中。然后,更新逻辑过期时间,同时更新物理过期时间。
流程如图:在该例子中,线程1发现数据的逻辑时间过期后,申请获取互斥锁,在申请成功后,重新创建一个线程——线程2用于更新缓存数据,但线程1不必等到线程2执行结束后再继续执行,而是获取缓存中的旧数据(没有到达物理过期时间,因此还可以获取该数据)继续执行;线程3运行时,也发现数据的逻辑时间已过期,但此时线程1已拿到了互斥锁,因此,线程3也是通过获取缓存中的旧数据继续执行;线程4运行时,线程2已经完成了数据更新,因此线程4拿到的是刚从数据库加载的新数据。
特点: 高可用,性能优。
缓存雪崩
缓存雪崩是指在使用缓存系统时,大量的缓存数据在同一时间段内失效或者Redis服务宕机,导致大量的请求同时涌入数据库进行查询,从而给底层存储系统造成短时间内的巨大压力,甚至导致系统崩溃的现象。
缓存雪崩发生情况如下:
-
大规模缓存失效:当缓存中的大量数据同时过期或者失效时,例如缓存系统因为某种原因(如重启、内存溢出、网络故障等)而导致所有缓存数据失效。
-
相同的过期时间:当缓存中的数据设置了相同的过期时间,并且这些数据又在同一时间段内失效时,可能会导致大量请求同时涌入底层存储系统。
-
并发访问高峰期:当系统处于高并发访问的时候,例如热门活动、促销活动、秒杀活动等,大量的请求同时访问相同的缓存数据,可能会导致缓存数据同时失效。
避免缓存雪崩的策略:
-
设置随机过期时间:避免所有缓存数据同时过期,可以在缓存数据的过期时间上增加一些随机性,使得缓存数据的过期时间分布在一个时间段内。
-
利用Redis集群提高服务的可用性:可以提高服务的可用性,并在一定程度上减轻缓存雪崩的影响,如哨兵模式(Sentinel)、集群模式(Cluster)。
-
给缓存业务添加降级限流策略:通过合理配置降级限流策略,可以在缓存系统出现异常或者压力过大时,及时采取措施来保护系统的稳定性和可用性,如Nginx、Spring Cloud Gateway。此策略可以作为系统的保底策略,适用于穿透、击穿和雪崩。
-
使用多级缓存:可以使用多级缓存架构,例如将热点数据放在内存缓存中,将冷数据放在分布式缓存或者数据库中,以减轻缓存失效带来的影响,如Caffeine、Guava。
相关补充
哨兵模式(Sentinel): 主要作用是监控 Redis 实例的健康状态,当主节点出现故障或不可用时,自动完成故障转移,并选择一个合适的从节点升级为新的主节点,以保证 Redis 服务的可用性,是Redis提供的一种高可用性解决方案。
工作原理:
-
哨兵节点部署:首先,需要在 Redis 集群中部署多个哨兵节点,这些哨兵节点相互独立,互相监控,并且可以通过相互通信来实现协作工作。
-
监控 Redis 实例状态:每个哨兵节点定期向 Redis 实例发送心跳检测请求,检测 Redis 实例的健康状态,包括主节点和所有从节点的连接状态、复制状态等。
-
故障检测与选举:当一个哨兵节点检测到主节点失效时,它会将主节点标记为下线状态,并向其他哨兵节点发送通知。其他哨兵节点收到通知后,也会进行故障检测,并开始进行选举。
-
选举新的主节点:哨兵节点通过一种基于投票的选举机制,选举一个合适的从节点升级为新的主节点。每个哨兵节点都可以发起投票,并且会向其他哨兵节点发送投票请求,收到最多选票的从节点将会被选举为新的主节点。
-
故障转移:选举完成后,哨兵节点会将新的主节点信息广播给所有客户端,并通知其他哨兵节点更新配置信息。客户端收到通知后,会重新连接到新的主节点,从而完成故障转移。
-
监控与恢复:新的主节点上线后,所有哨兵节点会持续监控 Redis 实例的运行状态,并定期发送心跳检测请求。如果发现新的主节点出现问题或者失效,哨兵节点会再次进行故障检测和选举,以保证 Redis 集群的持续可用。
集群模式(Cluster): 是一种用于提供高可用性和横向扩展的Redis分布式部署方案。在Redis集群模式中,数据被分片(sharding)并存储在多个节点上,同时提供故障检测、自动故障转移、数据重分配等功能,通过分片和节点间的协作,从而允许存储和处理大量数据,因此该模式可以提供高性能、高可用性和横向扩展的特性,适用于处理大规模数据和高并发访问的场景。
相关概念及工作原理:
-
分片(Sharding):
- Redis Cluster将整个数据集分割成16384个槽(slots)。
- 每个节点负责管理其中一部分槽的数据,当有新的节点加入集群或节点离开时,槽会被重新分配。
-
节点间通信:
- 集群中的每个节点都了解其他节点的信息,并通过集群总线进行通信,用于集群配置的传播、节点间的信息交换等。
-
故障检测与自动故障转移:
- 集群使用一种基于 Gossip 协议的去中心化的方式进行节点间的状态信息传播。
- 每个节点都会定期向集群中的随机节点发送PING消息,以检查对方是否在线。如果一个节点在一段时间内没有回应,则被标记为下线。
- 当主节点失效时,集群中的从节点会发起投票,选举一个从节点升级为新的主节点。
-
客户端路由:
- 客户端在连接Redis Cluster时,需要连接到至少一个集群节点,然后由该节点将请求路由到正确的目标节点。
- 客户端根据key计算出对应的槽,并将请求发送到负责该槽的节点上。
-
数据重分配:
- 当增加或删除节点时,Redis Cluster会重新分配槽,并将数据重新分片到新的节点上。
- 重分片过程中,集群会负责迁移槽上的数据,确保数据的完整性和一致性。
相关文献可参考:https://www.cnblogs.com/yidengjiagou/p/17345831.html
此时,已经流下了无知的泪~