在运维的工作中,Redis缓存击穿是什么?
在运维工作中,Redis缓存击穿是指某个热点键(Key)的缓存失效时,大量并发请求同时访问该数据,导致这些请求绕过缓存直接访问数据库,从而给数据库带来巨大压力,甚至可能导致数据库崩溃。
1. 缓存击穿的产生原因
- 热点数据失效:当某个热点数据的缓存过期时,大量请求涌入到数据库层,而此时数据库需要处理所有的请求,造成数据库的瞬时压力增大。
- 高并发场景:在高并发场景下,如电商秒杀活动或突发新闻事件,热点数据的缓存失效会导致大量请求同时冲击数据库。
2. 缓存击穿的危害
- 数据库压力过大:由于热点数据失效,导致瞬间的大量请求直接打到数据库,增加数据库的压力,可能会引发数据库连接耗尽、响应变慢等问题,严重时可能导致数据库宕机。
- 系统响应时间变长:数据库负载过高会导致查询响应时间变长,影响用户体验。
3. 缓存击穿的解决方案
3.1 互斥锁(Mutex Lock)
在缓存失效时,通过分布式锁(如 Redis 的 SETNX)让一个线程重建缓存,其他线程等待锁释放后重试。
import redis
import time
r = redis.Redis(host='localhost', port=6379, db=0)
def get_data_with_lock(key):
data = r.get(key)
if data is None:
lock_key = f"{key}_lock"
if r.setnx(lock_key, "1"):
r.expire(lock_key, 30) # 设置锁的过期时间,防止死锁
data = fetch_from_database(key)
r.set(key, data, ex=3600) # 设置缓存数据和过期时间
r.delete(lock_key) # 释放锁
else:
time.sleep(0.1) # 短暂等待后重试
return get_data_with_lock(key) # 递归调用,等待锁释放
return data
def fetch_from_database(key):
# 模拟从数据库加载数据
return f"value_{key}"
3.2 热点Key永不过期
对于一些极其热点的数据,可以设置永不过期,通过后台任务定期更新缓存。
import redis
import threading
import time
r = redis.Redis(host='localhost', port=6379, db=0)
def update_hot_data(key):
while True:
data = fetch_from_database(key)
r.set(key, data)
time.sleep(3600) # 每小时更新一次
def fetch_from_database(key):
# 模拟从数据库加载数据
return f"value_{key}"
# 启动后台线程更新热点数据
threading.Thread(target=update_hot_data, args=("hot_key",), daemon=True).start()
3.3 逻辑过期(Logical Expiration)
除了业务逻辑真正需要的字段之外,还额外设置一个字段用来判断缓存是否过期,一般为 expire。而key本身是不设置过期时间的。
import redis
import time
r = redis.Redis(host='localhost', port=6379, db=0)
def get_data_with_logical_expiration(key):
data = r.get(key)
if data:
data = eval(data) # 将字符串转换为字典
if time.time() > data['expire']: # 判断是否过期
lock_key = f"{key}_lock"
if r.setnx(lock_key, "1"):
r.expire(lock_key, 30) # 设置锁的过期时间,防止死锁
new_data = fetch_from_database(key)
new_expire = time.time() + 3600 # 设置新的过期时间
r.set(key, str({"value": new_data, "expire": new_expire})) # 更新缓存
r.delete(lock_key) # 释放锁
else:
time.sleep(0.1) # 短暂等待后重试
return get_data_with_logical_expiration(key) # 递归调用,等待锁释放
else:
data = fetch_from_database(key)
expire = time.time() + 3600 # 设置过期时间
r.set(key, str({"value": data, "expire": expire})) # 设置缓存
return data['value']
def fetch_from_database(key):
# 模拟从数据库加载数据
return f"value_{key}"
3.4 熔断降级
在缓存失效期间,启用降级策略(如返回默认值或静态页面),保护数据库。
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
def get_data_with_fallback(key):
data = r.get(key)
if data is None:
# 启用降级策略,返回默认值
return "Default Value"
return data
5.我的总结
综上所述,缓存击穿是指热点数据失效时,大量并发请求直接访问数据库的现象。通过使用互斥锁、热点Key永不过期、逻辑过期和熔断降级等方法,可以有效缓解缓存击穿带来的压力,确保系统的稳定性和可用性。在实际运维工作中,应根据具体业务需求选择合适的解决方案,确保缓存系统的高可用性和高性能。

浙公网安备 33010602011771号