缓存穿透、缓存击穿、缓存雪崩
缓存穿透
缓存穿透是指恶意或者不存在的请求通过缓存直接访问数据库或后端服务,由于缓存中没有对应的数据,导致大量的请求直接访问后端,造成后端压力过大。缓存穿透通常是由于缓存未命中造成的。
- 缓存穿透可能在访问量较大时出现,特别是当有大量的请求访问不同的、不存在于缓存中的数据时。
缓存击穿:缓存击穿是指一个热点key在缓存过期后,同时有大量请求访问该key,导致这些请求直接访问后端服务。缓存击穿通常发生在访问量极大的热点数据上。
- 缓存击穿可能在访问量较大时出现,特别是当有大量请求集中访问一个热点数据,并且这个热点数据恰好在缓存过期时。
缓存雪崩:缓存雪崩是指缓存中大量的数据在同一时间失效,导致大量请求直接访问后端服务。缓存雪崩通常是由于缓存中大量数据在同一时间失效或者过期导致的。
- 缓存雪崩可能在访问量较大时出现,特别是当缓存中大量数据在同一时间失效时。
解决方案
缓存穿透的解决方法:
使用布隆过滤器(Bloom Filter):布隆过滤器是一种数据结构,可以用于快速判断一个元素是否存在于一个集合中,可以有效地过滤掉不存在于缓存中的请求。
在缓存层增加空对象(Null Object)缓存:当数据库中查询的结果为空时,将空结果缓存起来,但设置一个合理的过期时间,避免恶意请求继续击穿数据库。
缓存击穿的解决方法:
使用互斥锁(Mutex Lock)或分布式锁:在缓存失效时,只允许一个线程去查询数据库,其他线程等待结果,避免大量请求直接访问后端服务。
提前异步更新缓存:在缓存即将过期时,异步更新缓存数据,避免大量请求同时对过期缓存进行访问。
缓存雪崩的解决方法:
缓存数据设置不同的过期时间:可以将不同的缓存数据设置不同的过期时间,避免同时失效,分散缓存的失效风险。
使用熔断机制:当缓存失效时,限制对后端服务的访问,等缓存恢复后再放开。
布隆过滤器
布隆过滤器不需要存储实际的元素数据,而是通过位数组和哈希函数来表示数据是否存在。然而,在实际应用中,布隆过滤器的内存占用仍然是需要考虑的。
布隆过滤器的内存占用主要取决于以下因素:
位数组大小:布隆过滤器内部使用一个位数组来表示数据是否存在,位数组的大小取决于预期存储的元素数量和期望的误判率。通常来说,误判率越低、需要存储的数据量越大,位数组的大小就越大。
哈希函数数量:布隆过滤器内部会使用多个哈希函数来计算元素的位置,哈希函数的数量也会对内存占用产生影响。
虽然布隆过滤器在理论上可以用较少的内存存储大量数据,但是误判率、存储的元素数量和哈希函数的数量等因素仍然会对内存占用产生影响。因此,在实际应用中,需要根据实际情况进行合理的参数选择,以及对内存占用进行评估和优化。
在大规模数据处理应用中,布隆过滤器通常被用于缓存穿透、数据去重等场景,在这些场景中,可以通过合理设置布隆过滤器的参数,来有效地节省内存占用。
具体python实现
- 缓存穿透:
'''缓存穿透是指当一个请求查询一个不存在的键时,由于缓存中没有相应的数据,每次请求都会穿透到数据库或其他持久化层,导致数据库压力过大。为了解决这个问题,可以在查询缓存之前添加一个布隆过滤器(Bloom Filter)来过滤掉不存在的键。'''
from django.core.cache import cache
from pybloom_live import BloomFilter
def get_data(key):
if key in cache:
return cache.get(key)
# Key not found in cache, check in Bloom Filter
bloom_filter = BloomFilter(capacity=10000, error_rate=0.1)
if key in bloom_filter:
return None # Key does not exist
# Key not found in Bloom Filter, query the database
data = query_database(key)
if data:
cache.set(key, data)
else:
bloom_filter.add(key)
return data
- 缓存击穿:
'''缓存击穿是指当一个热点数据过期或被删除时,大量并发请求同时访问该数据,导致所有请求都穿透到数据库。为了解决这个问题,可以在缓存失效时使用互斥锁(Mutex)来阻止多个线程同时查询数据库。'''
from django.core.cache import cache
from django.core.cache.backends.memcached import BaseMemcachedCache
def get_data(key):
data = cache.get(key)
if data is not None:
return data
lock_key = f'{key}:lock'
acquire_lock = lambda: cache.add(lock_key, 'locked', timeout=5)
release_lock = lambda: cache.delete(lock_key)
if acquire_lock():
try:
data = query_database(key)
if data:
cache.set(key, data)
finally:
release_lock()
return data
- 缓存雪崩:
'''缓存雪崩是指当多个缓存键同时失效或被删除时,大量并发请求同时访问这些数据,导致所有请求都穿透到数据库。为了解决这个问题,可以给每个缓存键添加一个随机的过期时间,使得缓存键的失效时间分散开。'''
from django.core.cache import cache
from random import randint
def get_data(key):
data = cache.get(key)
if data is not None:
return data
data = query_database(key)
if data:
# Add a random expiration time between 1 to 5 minutes
cache.set(key, data, timeout=randint(60, 300))
return data