对于缓存,我们主要关心两个:缓存的命中率,数据的一致性。由此又会有一些缓存引起的问题,缓存击穿、穿透、雪崩。对于这些问题也是我们在使用缓存时不得不考虑的 。这些问题的解决方案也有很多。这里简单列举几个:
(1)针对无效数据穿透,多级拦截:
无效数据,空数据,进行多种手段拦截,如布隆过滤器、业务参数有效性判断、缓存空值等等。多种手段拦截就是让流量最后无法穿透到最薄弱的底层服务。
(2)针对高并发热点key击穿,两阶段失效:
对于高并发的热点key,做逻辑和物理两阶段失效策略,逻辑失效前端响应仍然立即返回,仅异步排队去底层服务或DB获取数据并刷新缓存。
(3)缓存降级,限流排队,多级缓存:
缓存由于各种原因不可用,如果无任何措施,势必会出现雪崩的现象,为了尽可能保证业务服务可用或者部分可用,则必须对缓存允许降级,即便缓存中间件连接异常,宕机等,仍然可以去底层的服务和DB获取数据,常用手段一般是二级、甚至三级缓存,最后就是加锁排队访问底层服务。
(4)失效时间随机
往往热key都是在同一个时间点(短暂的时间段)创建的,如果固定有效时长,则失效将在同一个时间点,当失效发生时,雪崩的可能性很大。在业务要求的失效时间点上,加上随机时长(范围),可以分散热key失效时间点。
缓存击穿、穿透和雪崩是与缓存相关的常见问题。它们是在使用缓存时可能遇到的一些挑战和风险。下面是对这些问题的解释以及相应的实际例子:
1.缓存击穿: 缓存击穿指的是在缓存中不存在但是频繁被请求的数据,导致请求绕过缓存直接访问数据库或其他数据源,增加了后端负载。这通常发生在缓存中的数据过期或被删除时。
例子:假设一个电子商务网站的商品详情页被频繁访问,并且每次访问都会从数据库中获取商品信息。如果某个商品的缓存过期,而此时有大量用户请求该商品的详情页,这些请求将绕过缓存直接访问数据库,导致数据库负载过高。
1.缓存穿透: 缓存穿透指的是请求的数据在缓存和数据源中都不存在,这将导致每次请求都需要访问数据源,增加了后端负载。这通常是由于恶意或无效的请求导致的。
例子:假设一个新闻网站允许用户根据新闻ID获取新闻内容。如果有恶意用户通过构造不存在的新闻ID来频繁请求新闻内容,而这些请求都无法命中缓存或找到相应的数据,每次请求都需要访问数据库,造成了无谓的资源浪费。
1.缓存雪崩: 缓存雪崩指的是缓存中大量的数据同时过期或失效,导致大量请求直接访问数据源,造成后端系统的压力过大甚至崩溃。
例子:假设一个电商网站的商品列表页的缓存时间都设置为相同的时间,当缓存过期时,大量用户同时请求商品列表页,这些请求将直接访问数据库,导致数据库负载激增,甚至可能导致数据库崩溃。
为了解决这些问题,可以采取以下措施:
•对于缓存击穿,可以通过在缓存中设置短暂的过期时间或使用互斥锁的方式来避免。当某个数据过期时,只允许一个请求去重新加载数据到缓存中,其他请求等待。
•对于缓存穿透,可以在缓存中设置一个特殊的值,表示数据不存在,并将该值缓存一段时间。这样,当恶意请求到来时,可以直接从缓存中返回这个特殊值,而不需要访问数据源。
•对于缓存雪崩,可以采用多级缓存、缓存数据的不同过期时间或使用热点数据预加载等策略。这样可以减少缓存同时失效的概率,分散请求对后端系统的冲击。
需要根据具体情况和使用的缓存系统来选择合适的解决方案。
布隆过滤器(Bloom Filter)
是一种空间效率很高的概率型数据结构,用于判断一个元素是否属于一个集合。它通过使用多个哈希函数和位数组来判断元素是否存在,可以快速地进行查找和过滤。
以下是一个简单的布隆过滤器的示例代码:
python
CopyReplace
import mmh3from bitarray import bitarray
classBloomFilter:
def__init__(self, size, hash_functions):
self.size = size
self.hash_functions = hash_functions
self.bit_array = bitarray(size)
self.bit_array.setall(0)
defadd(self, item):
for fn in self.hash_functions:
index = mmh3.hash(item, fn)% self.size
self.bit_array[index]=1
defcontains(self, item):
for fn in self.hash_functions:
index = mmh3.hash(item, fn)% self.size
if self.bit_array[index]==0:
returnFalse
returnTrue
# 创建一个布隆过滤器实例
size =10 # 位数组的大小
hash_functions =[1,2,3] # 哈希函数的数量
bloom_filter = BloomFilter(size, hash_functions)
# 添加元素到布隆过滤器
bloom_filter.add("apple")
bloom_filter.add("banana")
bloom_filter.add("orange")
# 检查元素是否存在于布隆过滤器print(bloom_filter.contains("apple")) # 输出: Trueprint(bloom_filter.contains("grape")) # 输出: False
在这个示例中,BloomFilter
类表示布隆过滤器。在初始化过程中,我们指定了位数组的大小和哈希函数的数量。bitarray
库用于创建位数组,并使用mmh3
哈希函数库来生成哈希值。
add
方法用于将元素添加到布隆过滤器中。它使用每个哈希函数对元素进行哈希,并将对应的位数组位置置为1。
contains
方法用于检查元素是否存在于布隆过滤器中。它使用每个哈希函数对元素进行哈希,并检查对应的位数组位置是否为1。如果有任何一个位置为0,则说明元素不在布隆过滤器中。
在示例中,我们创建了一个布隆过滤器实例,添加了几个元素,并检查了其中的一些元素是否存在于布隆过滤器中。
需要注意的是,布隆过滤器存在一定的误判率(False Positive),即可能会判断某个元素存在于集合中,但实际上并不存在。因此,在使用布隆过滤器时,需要根据具体应用场景和数据量来选择合适的位数组大小和哈希函数数量,以控制误判率。