作者信息:https://home.cnblogs.com/u/huangjiabobk

在运维的工作中,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永不过期、逻辑过期和熔断降级等方法,可以有效缓解缓存击穿带来的压力,确保系统的稳定性和可用性。在实际运维工作中,应根据具体业务需求选择合适的解决方案,确保缓存系统的高可用性和高性能。

posted @ 2025-04-05 23:26  黄嘉波  阅读(70)  评论(0)    收藏  举报
版权声明:原创作品,谢绝转载!否则将追究法律责任。--作者 黄嘉波