缓存面试解析:穿透、击穿、雪崩,一致性、分布式锁、Redis过期,海量数据查找

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
简介: 本文提供了一些保证数据一致性和设计分布式锁的策略。这些策略可以在实际应用中帮助开发人员解决相关的问题,确保系统的数据一致性和并发访问的正确性。同时,通过合理地使用缓存和分布式锁,可以提高系统的性能和可靠性。希望对你在面对Redis相关面试题时有所帮助!

为什么使用缓存

  1. 在程序内部使用缓存,比如使用map等数据结构作为内部缓存,可以快速获取对象。通过将经常使用的数据存储在缓存中,可以减少对数据库的频繁访问,从而提高系统的响应速度和性能。缓存可以将数据保存在内存中,读取速度更快,能够大大缩短数据访问的时间,提升用户体验。
  2. 在业界中,通常在数据库之前添加一层Redis缓存,这样可以避免数据库的性能被大量的请求耗费。当有大量的并发请求时,数据库可能会成为瓶颈,而使用缓存可以有效地缓解数据库的压力。Redis作为一种高效的缓存解决方案,可以将热门数据存储在内存中,以快速响应用户的请求。这种缓存层的引入不仅可以提高系统的性能和吞吐量,还可以提高系统的可靠性和稳定性,因为即使数据库出现故障,缓存仍然可以提供部分服务。
  3. 缓存还可以减少网络传输的负载,特别是在分布式系统中。通过将计算结果或频繁访问的数据缓存起来,可以避免重复计算和重复访问数据库,节省了网络带宽和服务器的资源消耗。这对于海量数据的查找和计算密集型任务尤为重要,可以大大提升系统的效率和可扩展性。

    总之,使用缓存可以优化系统的性能、提高响应速度、降低数据库负载、节省网络传输和服务器资源,从而提升用户体验和系统的可靠性。

缓存穿透、击穿、雪崩

缓存穿透:

缓存穿透指的是当一个请求查询的数据不在缓存中,也不在数据库中,导致每次请求都直接访问数据库,增加了数据库的负载。这可能是由于恶意攻击或者异常情况导致的。为了解决缓存穿透问题,可以采取以下措施:

  • 在缓存中存储一个空值或者默认值,且设置成一定过期时间,以避免重复的无效查询,但是这种方案有缺陷就是redis会多出无用的key,浪费内存资源;
  • 使用布隆过滤器等技术来过滤掉无效的请求,将可能不存在的数据快速过滤掉,布隆过滤器可以有效防止不存在的key进入业务调用数据库,但是需要提前将数据库数据预热到布隆过滤器中,并且他也有一种缺陷就是由于他的数据结构和算法导致无法删除热键,只能新增;
    image

缓存击穿

缓存击穿指的是当某个热点数据过期或者被删除时,大量的请求同时涌入,导致数据库负载过高。这通常发生在高并发环境下。为了避免缓存击穿问题,可以采取以下措施:

  • 第一种就是将热点数据永久缓存进redis,并另起一个线程定时的去更新这个热点数据,那么就热点数据永远不会失效,但是缺陷是在定时任务启动前可能存在数据错误的情况;
  • 第二种情况那么就是加锁,使用互斥锁或者分布式锁来保护对数据库的访问,确保只有一个请求能够重新加载数据到缓存中。但是这种虽然解决了数据库问题,但同时也带来了性能下降;

缓存雪崩

缓存雪崩指的是当缓存中大量的数据同时过期时,导致大量的请求直接访问数据库,造成数据库负载过高。这通常是由于缓存服务器故障、网络故障或者缓存数据过期时间设置不合理等原因导致的。为了避免缓存雪崩问题,可以采取以下措施:

  • 就是在给缓存数据设置过期时间的时候请加一个随机值使用不同的过期时间来分散缓存的失效时间,避免大量数据同时过期。
  • 使用热点数据预加载技术,在缓存数据即将过期之前,提前加载数据到缓存中,确保数据的可用性。

如何保证缓存与数据库之间的数据一致性

保证缓存与数据库之间的强一致性是一个相对复杂的问题。尽管没有绝对的解决方案,但可以采取一些策略来尽可能地提高数据一致性。以下是几种常见的策略:

第一种就是先删除缓存还是先写数据库,这两种都一样,我就说下先删除缓存带来的问题,先删除缓存确实可以在写完数据库后后续的操作都会更新缓存值,但是扛不住并发高,如果删除完缓存后还没来得及写入又被另一个线程读取了旧值更新缓存,那么这缓存白删除了,

第二种就是先写数据库呢?如果数据库写完后,一是在删除缓存之前的读操作读取的仍然是旧值,二是,如果写操作完成后,缓存删除操作由于网络原因丢失了怎么办,以后读取操作都是旧值了;

第三种也就是业界最常用的延时双删;但同时他也无法一定保证数据的一致性

  • 在操作数据库之前先删除缓存:首先,你需要先删除缓存中对应的数据,确保下一次读取请求不会命中旧的缓存数据。
  • 更新数据库:然后,你可以更新数据库中的数据,确保数据库中的数据是最新的。
  • 再次删除缓存:最后,在延时之后,再次删除缓存中的数据。这样可以确保在延时结束后,读操作仍然可以从缓存中获取最新的数据。

如果写操作很频繁,那么缺陷就很明显:很容易产生脏数据并且也无法满足缓存与数据库之间的一致性;

第四种:引入MQ,当我们有两个消费者的时候,一个消费者只管消息的数据库操作,一个消费者只管消息的缓存操作,这样可以确保操作是原子操作。确保了不会删除缓存失败的问题。

但是以上四种都无法保证缓存与数据库之间的强一致性,只能保证数据库与缓存之间的最终一致性;

如何设计分布式锁?如何对锁性能进行优化?

首先分布式锁主要应用场景就是应对多节点部署下如何控制资源的并发保护,那么单纯的jvm锁已经无法满足需求,所以引入了分布式锁,那么常见的有数据库、zookeeper、redis;通常分布式锁的要求的就是性能高、与业务无关;设计分布式锁时,常见的选择是使用Redis作为分布式锁的存储介质。下面将介绍如何设计分布式锁,并对锁性能进行优化。

首先,我们需要掌握Redis的基本命令:

  • SETNX:设置键值对,如果键不存在则返回1,如果键已存在则返回0。
  • EXPIRE:设置键的过期时间。
  • GETSET:先获取旧值,然后将新值设置进去;如果键不存在,则返回null。
  • DEL:删除一个键。

    接下来,我们将讨论几种常见的分布式锁设计方式:

  1. 使用SETNX和DEL操作:在当前业务执行完毕后,使用DEL操作删除锁。但是如果获取锁的进程执行失败,它将永远不会主动解锁,导致锁被死锁。
  2. 使用SETNX和EXPIRE操作:这是最常见的分布式锁设计方式。但是存在一个问题,如果在设置过期时间之前节点挂掉,其他服务将永远无法获取到锁,因为SETNX和EXPIRE不是原子操作。
  3. 使用SETNX和GETSET操作:在设置锁时,将过期时间作为值存储在Redis中。当其他线程争取锁失败时,可以通过GETSET操作检查当前锁是否已经失效。如果锁已失效,则可以使用自己的过期时间来替换旧的值,并与之前的过期时间进行比较,以确定是否成功获取锁。下面给出伪代码示例:
public boolean tryLock(RedisConnection conn) {
   
   
    long nowTime = System.currentTimeMillis();
    long expireTime = nowTime + 1000;
    if (conn.SETNX("mykey", expireTime) == 1) {
   
   
        conn.EXPIRE("mykey", 1000);
        return true;
    } else {
   
   
        long oldValue = conn.get("mykey");
        if (oldValue != null && oldValue < nowTime) {
   
   
            long currentValue = conn.GETSET("mykey", expireTime);
            if (oldValue == currentValue) {
   
   
                conn.EXPIRE("mykey", 1000);
                return true;
            }
            return false;
        }
        return false;
    }
}

上述代码实现了一种比较高效的分布式锁。然而,上述优化的根本问题在于SETNX和EXPIRE两个指令无法保证原子性。为此,Redis 2.6版本引入了执行Lua脚本的功能,通过Lua脚本可以保证原子性。Redission工具就是基于此原理提供的分布式锁工具。

如何设置过期时间,实现原理是什么?

redis有两种命令可以进行对key设置过期时间:expire和setex。这两种命令都可以用来给key设置过期时间。

实现过期时间的原理可以分为两个部分。

首先是主动删除。Redis会有一个定时任务,定期检查数据库中的key是否已经过期。如果发现某个key已经过期,那么Redis会直接将其删除。

其次是被动删除。当应用程序尝试获取一个已经设置了过期时间的key时,Redis会检查该key是否已经过期。如果已经过期,Redis会在返回结果之前将该key删除。

这样,通过主动删除和被动删除的组合,Redis实现了对key的过期时间的管理。这种混合实现的方式可以保证Redis中的数据始终是最新的,并且不会出现过期的数据。

需要注意的是,Redis并不会为每个key都启动一个单独的定时任务去检查过期时间。相反,Redis会根据实际情况动态调整定时任务的执行频率,以提高性能和效率。这种设计可以有效地减少对系统资源的占用,提高Redis的性能和稳定性。

海量数据下,如何快速查找一条记录?

当前这道题目考验的是对redis整体的理解,所以也要全方位考虑,可以考虑以下优化策略:

  • 使用布隆过滤器:布隆过滤器是一种概率型数据结构,可以用于判断某个元素是否存在于集合中。在海量数据下,可以先使用布隆过滤器将不存在的key过滤掉,这样可以减少部分请求,提高查询效率。

  • 合理选择存储结构:在缓存记录时,可以考虑使用适合的存储结构。如果存储的是大对象,使用key+value(json)形式,那么key可能会很大,不建议使用。而如果使用hash结构存储,可以充分利用Redis的哈希表特性,提高存储效率。此外,可以根据实际情况选择其他存储结构,如列表、有序集合等。

  • 查询优化:如果Redis是集群部署的,数据根据槽位进行分配。如果我们自己对key进行了定位,可以直接访问对应的Redis节点,而不需要通过集群路由。这样可以减少Redis集群的机器计算,提高查询性能。

总结

本文提供了一些保证数据一致性和设计分布式锁的策略。这些策略可以在实际应用中帮助开发人员解决相关的问题,确保系统的数据一致性和并发访问的正确性。同时,通过合理地使用缓存和分布式锁,可以提高系统的性能和可靠性。希望对你在面对Redis相关面试题时有所帮助!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
24天前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
26天前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
60 3
|
9天前
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
66 22
|
8天前
|
缓存 NoSQL 中间件
redis高并发缓存中间件总结!
本文档详细介绍了高并发缓存中间件Redis的原理、高级操作及其在电商架构中的应用。通过阿里云的角度,分析了Redis与架构的关系,并展示了无Redis和使用Redis缓存的架构图。文档还涵盖了Redis的基本特性、应用场景、安装部署步骤、配置文件详解、启动和关闭方法、systemctl管理脚本的生成以及日志警告处理等内容。适合初学者和有一定经验的技术人员参考学习。
66 7
|
12天前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
44 10
|
12天前
|
缓存 监控 NoSQL
Redis 缓存穿透的检测方法与分析
【10月更文挑战第23天】通过以上对 Redis 缓存穿透检测方法的深入探讨,我们对如何及时发现和处理这一问题有了更全面的认识。在实际应用中,我们需要综合运用多种检测手段,并结合业务场景和实际情况进行分析,以确保能够准确、及时地检测到缓存穿透现象,并采取有效的措施加以解决。同时,要不断优化和改进检测方法,提高检测的准确性和效率,为系统的稳定运行提供有力保障。
42 5
|
12天前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
34 4
|
13天前
|
缓存 NoSQL Java
有Redis为什么还要本地缓存?谈谈你对本地缓存的理解?
有Redis为什么还要本地缓存?谈谈你对本地缓存的理解?
37 0
有Redis为什么还要本地缓存?谈谈你对本地缓存的理解?
|
27天前
|
消息中间件 中间件 数据库
NServiceBus:打造企业级服务总线的利器——深度解析这一面向消息中间件如何革新分布式应用开发与提升系统可靠性
【10月更文挑战第9天】NServiceBus 是一个面向消息的中间件,专为构建分布式应用程序设计,特别适用于企业级服务总线(ESB)。它通过消息队列实现服务间的解耦,提高系统的可扩展性和容错性。在 .NET 生态中,NServiceBus 提供了强大的功能,支持多种传输方式如 RabbitMQ 和 Azure Service Bus。通过异步消息传递模式,各组件可以独立运作,即使某部分出现故障也不会影响整体系统。 示例代码展示了如何使用 NServiceBus 发送和接收消息,简化了系统的设计和维护。
42 3
|
25天前
|
NoSQL Redis 数据库
计数器 分布式锁 redis实现
【10月更文挑战第5天】
44 1

相关产品

  • 云数据库 Tair(兼容 Redis)
  • 推荐镜像

    更多
    下一篇
    无影云桌面