文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

面试题:什么是缓存击穿、缓存穿透和缓存雪崩?它们分别会带来什么危害?该如何解决和预防?

2024-11-28 14:26

关注

一、面试官:请说说看什么是缓存击穿,他会带来什么危害,以及该如何解决?

1.缓存击穿

(1) 定义:缓存击穿是指在高并发访问下,某个热点数据的缓存过期失效,而此时恰好有大量并发请求访问该数据,导致这些请求直接绕过缓存,访问后端数据库或存储系统,使数据库或存储系统负载急剧增加,甚至可能引发系统崩溃的现象。

(2) 危害:

(3) 解决方案:

① 互斥锁(Mutex)和分布式锁(在分布式系统中):

在缓存失效时,使用互斥锁机制确保只有一个请求能够访问数据库并更新缓存,其他请求则等待锁释放后从缓存中获取数据。

下面是一个使用 Redis 分布式锁和 Redis 事务来解决缓存击穿问题的具体代码示例(Python实现)。


import redis
import time
import uuid

# 连接到 Redis 服务器
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

# 设置分布式锁的键和过期时间(秒)
LOCK_KEY = 'cache_击穿_lock'
LOCK_EXPIRE = 10  # 锁的有效期,可以根据需要调整

# 缓存的键和值(示例)
CACHE_KEY = 'some_hot_data'

def acquire_lock(redis_client, lock_key, lock_value, expire):
    """
    尝试获取分布式锁
    :param redis_client: Redis 客户端
    :param lock_key: 锁的键
    :param lock_value: 锁的值(通常是唯一标识符)
    :param expire: 锁的过期时间(秒)
    :return: 是否成功获取锁
    """
    while True:
        # 尝试设置锁,NX 表示只有键不存在时才设置,PX 表示过期时间(毫秒)
        result = redis_client.set(lock_key, lock_value, nx=True, px=expire * 1000)
        if result:
            return True
        # 休眠一小段时间后重试,避免忙等待
        time.sleep(0.01)

def release_lock(redis_client, lock_key, lock_value):
    """
    释放分布式锁
    :param redis_client: Redis 客户端
    :param lock_key: 锁的键
    :param lock_value: 锁的值(必须是获取锁时使用的相同值)
    :return: 是否成功释放锁
    """
    # 使用 Lua 脚本确保原子性释放锁
    lua_script = """
    if redis.call("get", KEYS[1]) == ARGV[1] then
        return redis.call("del", KEYS[1])
    else
        return 0
    end
    """
    redis_client.eval(lua_script, 1, lock_key, lock_value)
    return True

def get_data_with_cache_and_lock(redis_client, cache_key):
    """
    使用缓存、分布式锁和 Redis 事务获取数据
    :param redis_client: Redis 客户端
    :param cache_key: 缓存的键
    :return: 数据值或 None(如果数据不存在)
    """
    # 尝试从缓存中获取数据
    cache_value = redis_client.get(cache_key)
    if cache_value is not None:
        return cache_value.decode('utf-8')  # 假设数据是字符串类型

    # 尝试获取分布式锁
    lock_value = str(uuid.uuid4())
    if acquire_lock(redis_client, LOCK_KEY, lock_value, LOCK_EXPIRE):
        try:
            # 使用 Redis 事务确保原子性
            pipe = redis_client.pipeline(True)
            try:
                # 尝试再次从缓存中获取数据(防止其他客户端在获取锁后更新了缓存)
                pipe.watch(cache_key)
                cache_value = pipe.get(cache_key)
                if cache_value is not None:
                    pipe.unwatch()
                    pipe.reset()
                    return cache_value.decode('utf-8')

                # 从数据库中获取数据(模拟)
                # 在实际应用中,这里应该是访问数据库的逻辑
                data_from_db = "data_from_db"  # 假设从数据库中获取的数据

                # 更新缓存
                pipe.multi()
                pipe.set(cache_key, data_from_db)
                pipe.execute()

                # 返回从数据库中获取的数据
                return data_from_db

            except redis.WatchError:
                # 如果在事务执行过程中,缓存被其他客户端更新,则重新尝试获取数据
                pass

        finally:
            # 释放锁
            release_lock(redis_client, LOCK_KEY, lock_value)

    # 如果无法获取锁或缓存仍然为空,则返回 None(或根据业务逻辑返回默认值)
    return None

# 示例调用
data = get_data_with_cache_and_lock(redis_client, CACHE_KEY)
print(f"获取的数据: {data}")

② 热点数据永不过期:

对于重要的热点数据,可以设置其永不过期,以避免缓存过期引发的击穿问题。

但需要注意数据更新时的及时性和准确性,以及可能带来的内存占用问题。

③ 提前异步刷新缓存:

在缓存即将过期之前,通过定时任务或后台线程提前异步加载缓存数据,确保在缓存失效之前已经有新的数据加载到缓存中。

二、面试官:再说说看什么是缓存穿透,如何检测是否存在缓存穿透,以及该如何解决?

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

如果数据库查询不到这条数据,则不会写入缓存,这将导致这个不存在的数据每次请求都会去查询数据库,对数据库造成很大的压力。

缓存穿透通常是由恶意用户或攻击者请求不存在于缓存和后端存储中的数据来发起的攻击。

一般来说,缓存穿透一开始会由开发者发现系统接口变慢或监控告警发觉,再检测系统日志证实。检查数据库访问日志和缓存访问日志,查看是否存在大量对不存在的键的查询。这些查询如果频繁发生,那么很可能是缓存穿透。

另外,监控缓存的命中率。如果命中率突然下降,且伴随着数据库访问量的增加,这可能是缓存穿透的征兆。

再者,可以分析系统接收到的请求参数,特别是那些明显不符合业务逻辑的非法参数。

以下是两种防止缓存穿透的策略:

1.布隆过滤器(Bloom Filter)

布隆过滤器是一种空间效率很高的数据结构,它利用多个哈希函数来将一个元素映射到一个位数组的多个位中。当查询一个元素时,它会检查对应的位是否都为1,如果是,则认为元素可能存在(注意是可能存在,因为存在哈希冲突的情况),否则认为元素一定不存在。

在缓存穿透的场景中,可以在查询缓存之前先使用布隆过滤器检查元素是否存在。如果布隆过滤器认为元素不存在,则直接返回一个错误信息或默认值,而不去查询数据库。这样可以有效减少对数据库的无效查询。

布隆过滤器的缺点:

2.空值缓存(并不推荐):

对于那些查询结果为空的数据,也将其缓存起来,但设置一个较短的过期时间。这样,当下次再次查询这个不存在的数据时,可以直接从缓存中获取空值,而不是去查询数据库。

控制缓存的缺点:

三、面试官:什么是缓存雪崩,缓存雪崩产生的常见原因有哪些?

缓存雪崩是指在分布式系统中,缓存中的大量数据同时失效或过期,导致大量请求直接访问数据库或后端服务,造成系统性能急剧下降甚至瘫痪的现象。

这种现象通常会对系统造成灾难性的影响,因为它会导致后端数据库或服务承受巨大的压力,可能引发服务不可用或数据丢失等问题。

雪崩和击穿、热key的问题不太⼀样的是,他是指⼤规模的缓存都过期失效了。

缓存雪崩产生的常见原因主要包括以下几点:

为了预防缓存雪崩的发生,可以采取以下措施:

来源:程序员阿沛内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯