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

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
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相关面试题时有所帮助!

相关文章
|
1月前
|
存储 缓存 NoSQL
Redis常见面试题全解析
Redis面试高频考点全解析:从过期删除、内存淘汰策略,到缓存雪崩、击穿、穿透及BigKey问题,深入原理与实战解决方案,助你轻松应对技术挑战,提升系统性能与稳定性。(238字)
|
1月前
|
数据采集 监控 NoSQL
优化分布式采集的数据同步:一致性、去重与冲突解决的那些坑与招
本文讲述了作者在房地产数据采集项目中遇到的分布式数据同步问题,通过实施一致性、去重和冲突解决的“三板斧”策略,成功解决了数据重复和同步延迟问题,提高了系统稳定性。核心在于时间戳哈希保证一致性,URL归一化和布隆过滤器确保去重,分布式锁解决写入冲突。
116 2
 优化分布式采集的数据同步:一致性、去重与冲突解决的那些坑与招
|
28天前
|
消息中间件 运维 监控
《聊聊分布式》BASE理论 分布式系统可用性与一致性的工程平衡艺术
BASE理论是对CAP定理中可用性与分区容错性的实践延伸,通过“基本可用、软状态、最终一致性”三大核心,解决分布式系统中ACID模型的性能瓶颈。它以业务为导向,在保证系统高可用的同时,合理放宽强一致性要求,并借助补偿机制、消息队列等技术实现数据最终一致,广泛应用于电商、社交、外卖等大规模互联网场景。
|
2月前
|
存储 监控 NoSQL
Redis高可用架构全解析:从主从复制到集群方案
Redis高可用确保服务持续稳定,避免单点故障导致数据丢失或业务中断。通过主从复制实现数据冗余,哨兵模式支持自动故障转移,Cluster集群则提供分布式数据分片与水平扩展,三者层层递进,保障读写分离、容灾切换与大规模数据存储,构建高性能、高可靠的Redis架构体系。
|
2月前
|
存储 缓存 NoSQL
Redis持久化深度解析:数据安全与性能的平衡艺术
Redis持久化解决内存数据易失问题,提供RDB快照与AOF日志两种机制。RDB恢复快、性能高,但可能丢数据;AOF安全性高,最多丢1秒数据,支持多种写回策略,适合不同场景。Redis 4.0+支持混合持久化,兼顾速度与安全。根据业务需求选择合适方案,实现数据可靠与性能平衡。(238字)
|
5月前
|
监控 算法 关系型数据库
分布式事务难题终结:Seata+DRDS全局事务一致性架构设计
在分布式系统中,CAP定理限制了可用性、一致性与分区容错的三者兼得,尤其在网络分区时需做出取舍。为应对这一挑战,最终一致性方案成为常见选择。以电商订单系统为例,微服务化后,原本的本地事务演变为跨数据库的分布式事务,暴露出全局锁失效、事务边界模糊及协议差异等问题。本文深入探讨了基于 Seata 与 DRDS 的分布式事务解决方案,涵盖 AT 模式实践、分片策略优化、典型问题处理、性能调优及高级特性实现,结合实际业务场景提供可落地的技术路径与架构设计原则。通过压测验证,该方案在事务延迟、TPS 及失败率等方面均取得显著优化效果。
321 61
|
3月前
|
存储 缓存 人工智能
Redis六大常见命令详解:从set/get到过期策略的全方位解析
本文将通过结构化学习路径,帮助读者实现从命令语法掌握到工程化实践落地的能力跃迁,系统性提升 Redis 技术栈的应用水平。
|
5月前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
373 41
|
4月前
|
存储 NoSQL 定位技术
Redis数据类型面试给分情况
Redis常见数据类型包括:string、hash、list、set、zset(有序集合)。此外还包含高级结构如bitmap、hyperloglog、geo。不同场景可选用合适类型,如库存用string,对象存hash,列表用list,去重场景用set,排行用zset,签到用bitmap,统计访问量用hyperloglog,地理位置用geo。
121 5
|
4月前
|
存储 缓存 NoSQL
Redis 核心知识与项目实践解析
本文围绕 Redis 展开,涵盖其在项目中的应用(热点数据缓存、存储业务数据、实现分布式锁)、基础数据类型(string 等 5 种)、持久化策略(RDB、AOF 及混合持久化)、过期策略(惰性 + 定期删除)、淘汰策略(8 种分类)。 还介绍了集群方案(主从复制、哨兵、Cluster 分片)及主从同步机制,分片集群数据存储的哈希槽算法。对比了 Redis 与 Memcached 的区别,说明了内存用完的情况及与 MySQL 数据一致性的保证方案。 此外,详解了缓存穿透、击穿、雪崩的概念及解决办法,如何保证 Redis 中是热点数据,Redis 分布式锁的实现及问题解决,以及项目中分布式锁
126 1

相关产品

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

    更多
  • DNS