Redis 技术在业务场景中的应用与常见面试要点解析

哈根达斯
2024-08-05 / 0 评论 / 13 阅读 / 正在检测是否收录...

文章主要围绕 Redis 展开,首先介绍了 Redis 在业务中的使用场景,如缓存、会话管理、消息队列等。接着详细阐述了 Redis 常见的面试问题,包括数据类型、缓存击穿/穿透/雪崩的定义及应对方法、布隆过滤器的概念及注意事项、数据双写一致性的保证方式、持久化方式(RDB 和 AOF)、缓存删除策略、分布式锁的实现、集群方式(主从复制、哨兵模式、Cluster 集群)以及 Redis 虽是单线程却速度快的原因等,为读者全面了解 Redis 提供了丰富的知识和参考

(一)Redis的业务使用场景

Redis 是一个开源的内存数据结构存储系统,具有高性能、丰富的数据结构和多种特性,在业务中经常做为中间来进行使用,场景中常见应用场景如下:

  • 缓存:对于一些频繁访问且相对静态的数据,比如电商网站中的商品基本信息、用户配置信息等,将其存储在 Redis 中。当应用程序需要获取这些数据时,首先从 Redis 中查询,如果 Redis 中不存在,再从数据库中查询并将结果存入 Redis,下次访问时就可以直接从 Redis 中获取,大大减少数据库的访问压力,提高响应速度
  • 会话管理:传统的 Web 应用中,会话信息通常存储在服务器的内存中或者使用 Cookie /Session存储。然而,当多服务器集群部署时,使用服务器内存存储会话会面临会话同步的问题,Redis 可以作为一个集中式的会话存储解决方案,将用户的会话信息存储在 Redis 中,便于多台服务器共享数据,避免重复登录或登录失效等问题
  • 消息队列: Redis 的列表数据结构可以用作简单的消息队列。生产者将任务或者消息以 LPUSH 命令推入 Redis 列表的一端,消费者使用 RPOP 或者 BRPOP 命令从列表的另一端取出任务或消息进行处理,在Java来说Redision第三方库直接可使用的队列类型进行使用
  • 发布/订阅模式:Redis 提供了发布/订阅功能,发布者将消息发布到特定的频道,订阅了该频道的所有客户端都可以收到消息。这种模式适用于实时性要求较高、一对多的消息通知场景,比如构建聊天室或者集群中广播接收可用该特性实现业务功能
  • 分布式锁:在分布式系统中,多个进程或线程可能需要访问共享资源,为了保证资源的互斥访问,可以使用 Redis 实现分布式锁。通过 SETNX 命令设置一个键值对,如果键不存在则设置成功并获取锁,处理完业务后通过 DEL 命令释放锁。
  • 限速器实现:利用 Redis 的过期时间和计数功能可以实现限速功能。例如,限制用户在一定时间内的请求次数,防止恶意请求或滥用。可以通过记录用户的请求时间戳或请求次数,并结合 Redis 的过期设置来控制访问频率
  • 排行榜: 如玩家得分排行榜、等级排行榜等可以使用 Redis 的有序集合来实现。玩家的游戏数据(如得分、等级等)作为有序集合的元素分值,玩家 ID 作为元素。通过 Redis 的 ZADD、ZRANK 等命令可以方便地添加玩家数据到排行榜、查询玩家的排名等操作。
  • 地理位置服务:Redis 从 3.2 版本开始支持地理位置相关的操作。在社交应用、外卖应用、打车应用等场景中,可以使用 Redis 存储用户或地点的地理位置信息,并通过相关命令查询附近的用户或地点。同时我们还可以利用 Redis 的地理位置功能可以实现地理围栏的应用。例如,在物流配送系统中,可以设置地理围栏区域,当配送车辆进入或离开某个区域时,触发相应的事件和操作

(二)Redis常见面试问题

1. Redis常见的数据类型有哪些?

答: 字符串类型(string),哈希(hash),列表(list),集合(set),有序集合(sorted set)

2. 什么是缓存击穿/穿透/雪崩是指什么,如何应对处理?

答:

  1. 缓存击穿:缓存击穿是指一个 key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 key 突然失效的瞬间,持续的大并发就穿破缓存,直接请求查询数据库,就像在一道坚固的屏障上击破了一个洞,处理方式有如下几个方式

    • 互斥锁:当发现缓存失效时,不是立即去访问数据库,而是先通过互斥锁(如 Java 中的 synchronized 或 ReentrantLock)控制只有一个线程去访问数据库,其他线程等待。获取数据后更新缓存,然后释放锁,其他等待的线程直接从缓存中获取数据,
    • 设置热点数据永不过期:对于一些特别热点且更新不频繁的数据,可以考虑不设置过期时间,这样就不会出现因为过期而导致的缓存击穿问题。但这种方法需要注意数据的更新操作,当数据更新时,需要同时更新缓存和数据库以保持一致性。
  2. 缓存穿透:缓存穿透是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据先查询缓存,缓存没有的话再去查询数据库,数据库没有的话就返回空结果。但当用户故意去查询一个数据库中不存在的数据时,缓存中自然也不会有,每次请求都会直接打到数据库,就像穿透了缓存直接打到数据库,处理方式有如下几个方式

    • 布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截,从而避免了对数据库的查询。当请求过来时,先通过布隆过滤器判断数据是否存在,如果不存在则直接返回,不再访问缓存和数据库

    lzgn934l.png

  • 空值缓存:当数据库查询结果为空时,也把这个空结果进行缓存(可以设置较短的过期时间,比如几分钟),这样下次相同的请求就不会再访问数据库,该方法实现简单,但是并不太推荐使用,可能会造成大量伪造访问导致Redis中存在大量空值缓存数据占用大量内存空间(配合key过期时间使用)
  1. 缓存雪崩: 缓存雪崩是指缓存中大量的 key 在同一时间过期或者缓存服务出现故障不可用,导致大量请求直接访问数据库,就像一场突如其来的雪崩,瞬间给数据库带来巨大压力,可能导致数据库崩溃,处理方式有如下几个方式

    • 分散过期时间:在设置缓存过期时间时,避免大量 key 集中在同一时间过期。可以给 key 的过期时间加上一个随机时间或者采用不同的过期策略,比如让一部分数据在高峰期前过期,另一部分在高峰期后过期。
    • 缓存降级:当缓存服务出现故障时,可以启动缓存降级策略。比如暂时停止一些不重要业务的数据缓存查询,优先保证核心业务的正常运行;或者设置一个默认的缓存数据返回给用户,减少对数据库的访问压力。
    • 多级缓存:构建多级缓存,除了使用 Redis 等集中式缓存,还可以在应用服务器本地使用本地缓存(如 Ehcache、Guava Cache 等)。当集中式缓存出现问题时,本地缓存还可以抵挡一部分请求,减轻数据库压力。
    • 实时监控与报警:建立对缓存和数据库的实时监控系统,当发现缓存命中率突然下降、数据库负载急剧上升等异常情况时,及时发出报警,以便运维人员能够快速响应和处理。
3. 什么布隆过滤器,使用的时候应该注意什么问题?

答: 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能快速判断一个元素是否属于这个集合,使用需要注意如下:

  • 误判问题: 布隆过滤器存在误判的可能,即当布隆过滤器判断一个元素在集合中时,该元素实际上可能不在集合中。这是因为不同元素的哈希值可能会碰撞到相同的位上。所以了解布隆过滤器的误判特性,在对准确性要求极高的场景下我们应该谨慎使用。对于可以容忍一定误判率的场景,如网页爬虫判断已访问的URL,这种误判是可以接受的
  • 哈希函数的选择:哈希函数的质量直接影响布隆过滤器的性能和误判率。如果哈希函数过于简单或相关性强,会增加哈希冲突的概率,导致误判率升高。应对措施:使用多个不同的哈希函数来降低冲突概率,常见的有MD5、SHA等哈希算法的组合使用。
  • 数据量预估和空间大小设置:初始设置的位数组空间过小,随着元素的不断添加,误判率会迅速增加;而空间过大则会浪费内存资源。
4. 如何保证数据双写一致性?

答: 使用什么方案需要根据业务场景分析使用合适的方式,常见的方式如下:

  • 延时双删:先删除 Redis 中的缓存数据,然后再更新数据库,更新数据库后,经过一定的延时(比如几百毫秒,具体时间需要根据业务和系统性能测试来确定)再次删除缓存,该方式会出现“延时期”时间内数据不一致的情况,适用于对数据一致性要求较高,能容忍一定时间内(延时期内)数据不一致的场景,比如一些电商系统中的商品信息、用户信息等非实时强一致性要求的数据更新。
  • 数据通知更新:当有数据更新操作时如使用'阿里canal'中间件,数据更新后将任务放入一个可靠的消息队列中,费者服务从队列中取出任务,先执行删除缓存操作,然后再进行数据库更新操作,确保操作的顺序性,适用于高并发的数据更新场景,尤其是多个不同系统或服务之间需要协同保证数据一致性的情况,但消息的处理存在一定的延迟,对实时性要求极高的场景可能不太适用

lzgna40x.png

  • 分布式锁:适用严格保证同一时间只有一个操作进行数据更新和缓存操作的场景,如金融系统中的关键数据更新、高并发的抢购活动中的库存数据更新等,比如一些电商系统中的商品信息、用户信息等非实时强一致性要求的数据更新场景。使用分布式锁机制会带来一定的性能开销,获取和释放锁需要一定的时间,因此会有一定的效率,常见使用锁为读写锁

lzgnaf9n.png

5. Redis的持久化有哪些方式?

答: Redis 持久化的方式主要有两种:RDB(Redis Database)和 AOF(Append Only File)

  • RDB 是 Redis 默认的持久化方式,它是一种快照(snapshot)形式的持久化,将某一时刻的内存数据全部写入到磁盘中,形成一个数据快照文件,可以手动执行 SAVE 或 BGSAVE 命令来生成 RDB 文件。SAVE 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕;BGSAVE 命令会在后台异步进行快照操作,不会阻塞服务器的正常处理,

    • 优点:文件紧凑,体积小,便于备份和恢复,
    • 缺点:数据的安全性相对较低。由于是间隔一段时间进行快照,如果在两次快照之间发生故障,会丢失这期间的数据
  • AOF 以日志的形式记录服务器所处理的每一个写操作命令,如 SET、SADD 等,在 Redis 服务器启动时会重新执行这些命令来恢复数据,过设置 appendfsync 选项来控制 AOF 同步的频率,有 always、everysec 和 no 三种选项。always 表示每个写操作都立即同步到 AOF 文件,数据安全性最高但性能影响较大;everysec 表示每秒同步一次,是默认选项,在性能和数据安全性之间取得平衡;no 表示由操作系统来控制何时同步,速度最快但数据安全性最低

    • 优点:数据安全性高。通过不同的同步频率设置,可以尽可能保证数据不丢失,即使在服务器崩溃时,最多也只会丢失一秒钟的数据
    • 缺点:AOF 文件通常比 RDB 文件大,因为它记录了每一个写操作命令,恢复数据的速度相对较慢
6. Redis缓存删除策略是什么?

答: 定期删除+惰性删除,

  • 定期删除:Redis 服务器每隔一段时间(默认是 100ms)就会从设置了过期时间的键集合中随机抽取一定数量的键进行检查,并删除其中的过期key,
  • 惰性删除:数据到期时不主动删除,而是在访问该键时,对其是否过期进行检查判断。如果发现键已过期,才执行删除操作;如果未过期或者键不存在,则正常进行其他操作
7. Redis如何实现分布式?

答: 使用 SETNX 命令实现简单分布式锁,SETNX(SET if Not eXists)是 Redis 的一个原子性操作命令。它尝试将一个键值对设置到 Redis 中,如果键不存在则设置成功并返回 1;如果键已经存在,则设置失败并返回,但实际业务中我们还要考虑锁的续期,死锁等问题

8. Redis集群了解吗,都有哪些集群方式?

答:

  • 主从复制:一个主节点(Master)可以有多个从节点(Slave),主节点负责处理写操作和数据同步到从节点,从节点主要负责处理读操作

lzgnax9x.png

    • 应用场景:适用于读操作远多于写操作的场景,通过多个从节点分担读负载,提高系统的读性能,可以作为数据备份的一种方式,当主节点出现故障时,从节点可以快速提升为主节点继续提供服务,增强数据的可靠性
    • 优点:实现相对简单,部署容易,可以通过增加从节点线性地扩展读性能,成本较低。
    • 缺点:写操作只能在主节点上进行,写性能受限于单个主节点,当主节点出现故障时,需要手动将从节点提升为主节点,存在一定的切换时间和数据丢失风险(取决于故障时的数据同步情况)
    • 哨兵模式集群:由 Redis 主从节点、哨兵节点(Sentinel)组成,哨兵节点是特殊的 Redis 实例,不存储数据,主要用于监控 Redis 主从节点的运行状态。

    lzgnbbpb.png

    • 应用场景:适用于对高可用性要求较高的场景,能够自动进行故障切换,减少人工干预,在主从复制的基础上,进一步提高了系统的可靠性和稳定性
    • 优点:实现了自动故障检测和转移,提高了系统的可用性,配置相对简单,客户端可以通过哨兵节点自动获取主节点信息
    • 缺点:仍然存在单个主节点的写性能瓶颈问题,当进行故障转移时,可能会出现短暂的服务中断(虽然时间较短,但对于某些对实时性要求极高的应用可能会有影响
    • Cluster 集群(分片集群):采用去中心化的架构,没有中心节点,数据被自动分割到多个节点上,每个节点负责一部分数据,master之间通过ping监测彼此健康状态

    lzgncfw5.png

    • 应用场景: 适用于大规模数据存储和高并发访问的场景,能够满足海量数据和高吞吐量的需求,可以方便地进行横向扩展,通过增加节点来提高系统的存储能力和处理能力
    • 优点: 自动数据分片和负载均衡,提高了系统的扩展性和性能,去中心化架构,单个节点故障对系统的整体影响较小
    • 缺点: 配置和管理相对复杂,需要对数据分片和集群机制有深入的理解,数据迁移和槽重新分配时可能会对系统性能产生一定的影响
    9. Redis是单线程吗,为什么它还可以这么快?

    答: Redis 是单线程,但却很快。原因是它存于内存,内存读写比磁盘快很多,且有针对内存优化的数据结构,如 SDS、跳表、压缩列表。其次,采用高效事件驱动模型和非阻塞 I/O 多路复用,还有合适的数据淘汰策略。最后,单线程模型避免了线程切换开销和线程竞争问题,不用考虑同步机制带来的性能损耗,能将资源专注用于数据处理,这就是 Redis 虽单线程但速度快的主要原因。

    0

    评论 (0)

    取消