Redis 常见经典面试题

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
日志服务 SLS,月写入数据量 50GB 1个月
简介: Redis 常见经典面试题

槽位为什么设计为16384个

Redis集群模式下,会有多个节点,节点之间需要进行pingpong心跳通信,通信内容除了消息类型,节点id外,还会在消息主题中携带总结点数十分之一的节点信息进行交换,相互告知。如果槽位分配过大,会导致心跳通信的心跳包过大,浪费带宽且没必要,甚至容易造成网络拥堵。如果哈希槽设计过少,例如几千个的话,我们知道redis建议集群模式不要超过1000节点,那么如果是满载1000个节点,平分几千个哈希槽,在节点迁移时候容易出现数据倾斜,造成单节点访问过高,甚至造成节点连续崩溃,最终导致大规模服务集群雪崩相像。

Redis为什么快

  • Redis完全基于内存
  • Redis整个结构类似于HashMap,查找和操作复杂度为O(1),不需要和MySQL查找数据一样需要产生随机磁盘IO或者全表
  • Redis对于客户端的处理是单线程的,采用单线程处理所有客户端请求,避免了多线程的上下文切换和线程竞争造成的开销
  • 底层采用select/epoll多路复用的高效非阻塞IO模型
  • 客户端通信协议采用RESP,简单易读,避免了复杂请求的解析开销

缓存雪崩(同一时间大量热点key过期)

缓存雪崩造成的原因是因为我们在做缓存时为了保证内存利用率,一般在写入数据时都会给定一个过期时间,而就是因为过期时间的设置有可能导致大量的热点key在同一时间内全部失效,此时来了大量请求访问这些key,而redis中却没有这些数据,从而导致所有请求直接落入DB查询,造成DB出现瓶颈或者直接被打宕导致雪崩情况的发生。

关于解决方案的的话也可以从多个维度来考虑:

  • 设置热点数据永不过期,避免热点数据的失效导致大量的相同请求落入DB
  • 错开过期时间的设置,根据业务以及线上情况合理的设置失效时间
  • 使用分布式锁或者MQ队列使得请求串行化,从而避免同一时间请求大量落入DB(性能会受到很大的影响)

缓存穿透

请求参数不合理,导致Redis一直去数据库中查询。

缓存穿透这个问题是由于请求参数不合理导致的,比如对外暴露了一个接口getUser?userID=xxx,而数据库中的userID是从1开始的,当有黑客通过这个接口携带不存在的ID请求时,比如:getUser?userID=-1,请求会先来到Redis中查询缓存,但是发现没有对应的数据从而转向DB查询,但是DB中也无此值, 所以也无法写入数据到缓存,而黑客就通过这一点利用“肉鸡”等手段疯狂请求这个接口,导致出现大量redis不存在数据的请求落入DB,从而导致DB出现瓶颈或者直接被打宕机,整个系统陷入瘫痪。

解决方案:

  • 做IP限流与黑名单,避免同一IP瞬间发送大量请求
  • 对于请求做非法校验,对于携带非法参数的请求直接过滤
  • 对于DB中查询不存在的数据写入Redis中“Not Data”并设置短暂的过期时间,下次请求能够直接被拦截在redis而不会落入DB
  • 布隆过滤器

缓存击穿

一个热点key突然过期,数据库无法承受大量请求。

缓存击穿和缓存雪崩有点类似,都是由于请求的key过期导致的问题,但是不同点在于失效key的数量,对于雪崩而言指的是大量的key失效导致大量请求落入DB,而对于击穿而言,指的是某一个热点key突然过期,而这个时候又突然又大量的请求来查询它,但是在redis中却并没有查询到结果从而导致所有请求全部打向DB,导致在这个时刻DB直接被打穿。

解决方案的话也是有多种:

  • 设置热点key永不过期
  • 做好redis监控,请求串行化访问(性能较差)
  • 使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

redis淘汰策略

redis在5.0之前为我们提供了六种淘汰策略,而5.0为我们提供了八种,但是大体上来说这些lru、lfu、random、ttl四种类型

  • 在Redis中,数据有一部分访问频率较高,其余部分访问频率较低,或者无法预测数据的使用频率时,设置allkeys-lru是比较合适的。
  • 如果所有数据访问概率大致相等时,可以选择allkeys-random。
  • 如果研发者需要通过设置不同的ttl来判断数据过期的先后顺序,此时可以选择volatile-ttl策略。
  • 如果希望一些数据能长期被保存,而一些数据可以被淘汰掉时,选择volatile-lru或volatile-random都是比较不错的。
  • 由于设置expire会消耗额外的内存,如果计划避免Redis内存在此项上的浪费,可以选用allkeys-lru策略,这样就可以不再设置过期时间,高效利用内存了。
  • maxmemory-policy:参数配置淘汰策略。maxmemory:限制内存大小。

三种键删除策略

redis删除Key的策略策略有三种:

  • 定时删除:在设置键的过期时间的同时,设置一个定时器,当键过期了,定时器马上把该键删除。(定时删除对内存来说是友好的,因为它可以及时清理过期键;但对CPU是不友好的,如果过期键太多,删除操作会消耗过多的资源。)
  • 惰性删除:key过期后任然留在内存中不做处理,当有请求操作这个key的时候,会检查这个key是否过期,如果过期则删除,否则返回key对应的数据信息。(惰性删除对CPU是友好的,因为只有在读取的时候检测到过期了才会将其删除。但对内存是不友好,如果过期键后续不被访问,那么这些过期键将积累在缓存中,对内存消耗是比较大的。)
  • 定期删除:redis数据库默认每隔100ms就会进行随机抽取一些设置过期时间的key进行检测,过期则删除。(定期删除是定时删除和惰性删除的一个折中方案。可以根据实际场景自定义这个间隔时间,在CPU资源和内存资源上作出权衡。)

Redis三种持久化机制

Redis的确是将数据存储在内存的,但是也会有相关的持久化机制将内存持久化备份到磁盘,以便于重启时数据能够重新恢复到内存中,避免数据丢失的风险。而Redis持久化机制由三种,在4.X版本之前Redis只支持AOF以及RDB两种形式持久化,但是因为AOF与RDB都存在各自的缺陷,而在4.x版本之后redis还提供一种新的持久化机制:混合型持久化(但是最终生成的文件还是AOF)。

RDB 持久化

RDB 持久化把内存中当前进程的数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发:

  • save:阻塞当前 Redis,直到 RDB 持久化过程完成为止,若内存实例比较大 会造成长时间阻塞,线上环境不建议用它
  • bgsave:redis 进程执行 fork 操作创建子进程,由子进程完成持久化,阻塞时间很短(微秒级),是 save 的优化
  • bgsave过程:通过fork创建子进程,RDB 持久化过程由子进程负责,会在后台异步进行快照操作,快照同时还可以响应客户端请求

    而且RDB 是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,重启时加载这个文件达到数据恢复。

  • redis RDB持久化默认开启 save 900 1 -- 900s内存在1个写操作 save 300 10 -- 300s内存在10个写操作 save 60 10000 -- 60s内存在10000个写操作 如上是RDB的自动触发的默认配置,当操作满足如上条件时会被触发。
  • RDB优缺点:

    • 优点:使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能;而且RDB文件存储的是压缩的二进制文件,适用于备份、全量复制,可用于灾难备份,同时RDB文件的加载速度远超于AOF文件。
    • 缺点:RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候,因为RDB无法做到实时持久化,而且每次都要创建子进程,频繁创建成本过高;备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(需要的内存是原本的两倍);还有一点,RDB文件保存的二进制文件存在新老版本不兼容的问题。

AOF持久化

而AOF持久化方式能很好的解决RDB持久化方式造成的数据丢失,AOF持久化到硬盘中的并不是内存中的数据快照,而是和MySQL的belog日志一样记录写入命令,AOF的持久化策略也有三种:

  • appendfsync always:同步持久化形式,每次发生数据更改都将命令追加到AOF文件,因为每次写入时都记录会产生大量磁盘IO,从而性能会受到影响,但是数据最安全。
  • appendfsync everysec:redis开启AOF后的缺省配置,异步操作,每秒将写入命令追加到AOF文件,如果在刚持久化之后的一秒内宕机,会造成1S的数据丢失。
  • appendfsync no:redis并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据buffer填充情况/通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因 OS 配置有关。

AOF持久化机制优缺点:

  • 优点:根据不同的fsync策略可以保证数据丢失风险降到最低,数据能够保证是最新的,fsync是后台线程在处理,所以对于处理客户端请求的线程并不影响。
  • 缺点:文件体积由于保存的是所有命令会比RDB大上很多,而且数据恢复时也需要重新执行指令,在重启时恢复数据的时间往往会慢很多。虽然fsync并不是共用处理客户端请求线程的资源来处理的,但是这两个线程还是在共享同一台机器的资源,所以在高并发场景下也会一定受到影响。

AOF rewrite操作

随着redis在线上运行的时间越来越久,客户端执行的命令越来越多,AOF的文件也会越来越大,当AOF达到一定程度大小之后再通过AOF文件恢复数据是异常缓慢的,那么对于这种情况redis在开启AOF持久化机制的时候会存在AOF文件的重写,缺省配置是当AOF文件比上一次重写时的文件大小增长100%并且文件大小不小于64MB时会对整个AOF文件进行重写从而达到“减肥”的目的。

而AOF rewrite 操作就是“压缩”AOF 文件的过程,当然 redis 并没有采用“基于原 aof 文件”来重写的方式,而是采取了类似 snapshot 的方式:基于 copy-on-write,全量遍历内存中数据,然后逐个序列到aof 文件中。因此 AOF rewrite 能够正确反应当前内存数据的状态,这正是我们所需要的;rewrite 过程中,对于新的变更操作将仍然被写入到原 AOF 文件中,同时这些新的变更操作也会被 redis 收集起来。当内存数据被全部写入到新的 aof 文件之后,收集的新的变更操作也将会一并追加到新的 aof 文件中,此后将会重命名新的 aof 文件为 appendonly.aof, 此后所有的操作都将被写入新的 aof 文件。如果在rewrite 过程中,出现故障,将不会影响原 AOF 文件的正常工作,只有当 rewrite 完成之后才会切换文件,因为 rewrite 过程是比较可靠的,触发 rewrite 的时机可以通过配置文件来声明,同时 redis 中可以通过 bgrewriteaof 指令人工干预。

项目中redis采用的是那种持久化方式

在项目中如果redis中不仅仅只是用来做缓存,其中还存储着一些MySQL中不存在的数据,所以数据的安全性要求比较高,而RDB因为并不是实时的持久化,会出现数据丢失,但是采用AOF形式在重启、灾备、迁移的时候过程异常耗时,也并不理想,所以在我们线上是同时采用两种形式的。

线上双开加载顺序:

RDB和AOF同时开启时,会优先加载AOF文件,因为AOF文件的数据完整性是最高的,如果没有AOF文件,则加载RDB,存在RDB文件,则加载RDB文件,加载成功则启动成功。

4.x之后的混合型持久化

在redis4.x之后推出了混合型持久化机制,因为RDB虽然加载快但是存在数据丢失,AOF数据安全但是加载缓慢,redis为了解决这个问题,带来了一个新的持久化选项——混合持久化。

将RDB文件的内容和增量的AOF日志文件存在一起。这里的AOF日志不再是全量 的日志,而是自持久化开始到持久化结束的这段时间发生的增量AOF日志,通常这部分AOF日志很小。redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志,就可以完全替代之前的AOF全量文件重放,恢复效率因此大幅得到提升(混合型持久化最终生成的文件后缀是.aof,可以通过redis.conf文件中 aof-use-rdb-preamble yes 配置开启)。

混合型持久化优点:

结合了RDB和AOF的优点,使得数据恢复的效率大幅提升

混合型持久化缺点:

兼容性不好,redis-4.x新增,虽然最终的文件也是.aof格式的文件,但在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
9天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
1月前
|
存储 NoSQL Java
可能是最漂亮的Redis面试基础详解
我是南哥,相信对你通关面试、拿下Offer有所帮助。敲黑板:本文总结了Redis基础最常见的面试题!包含了Redis五大基本数据类型、Redis内存回收策略、Redis持久化等。相信大部分Redis初学者都会忽略掉一个重要的知识点,Redis其实是单线程模型。我们按直觉来看应该是多线程比单线程更快、处理能力更强才对,比如单线程一次只可以做一件事情,而多线程却可以同时做十件事情。但Redis却可以做到每秒万级别的处理能力,主要是基于以下原因:(1)Redis是基于内存操作的,Redis所有的数据库状态都保存在
可能是最漂亮的Redis面试基础详解
|
1月前
|
NoSQL Java API
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试一线互联网企业时遇到了关于Redis分布式锁过期及自动续期的问题。尼恩对此进行了系统化的梳理,介绍了两种核心解决方案:一是通过增加版本号实现乐观锁,二是利用watch dog自动续期机制。后者通过后台线程定期检查锁的状态并在必要时延长锁的过期时间,确保锁不会因超时而意外释放。尼恩还分享了详细的代码实现和原理分析,帮助读者深入理解并掌握这些技术点,以便在面试中自信应对相关问题。更多技术细节和面试准备资料可在尼恩的技术文章和《尼恩Java面试宝典》中获取。
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
|
1月前
|
NoSQL 算法 Redis
Redis面试篇
Redis面试篇
36 5
|
1月前
|
缓存 NoSQL Java
Java中redis面试题
Java中redis面试题
37 1
|
21天前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
2月前
|
存储 缓存 NoSQL
【Java面试题汇总】Redis篇(2023版)
Redis的数据类型、zset底层实现、持久化策略、分布式锁、缓存穿透、击穿、雪崩的区别、双写一致性、主从同步机制、单线程架构、高可用、缓存淘汰策略、Redis事务是否满足ACID、如何排查Redis中的慢查询
【Java面试题汇总】Redis篇(2023版)
|
1月前
|
NoSQL Redis
redis 的 key 过期策略是怎么实现的(经典面试题)超级通俗易懂的解释!
本文解释了Redis实现key过期策略的方式,包括定期删除和惰性删除两种机制,并提到了Redis的内存淘汰策略作为补充,以确保过期的key能够被及时删除。
55 1
|
2月前
|
缓存 监控 NoSQL
阿里面试让聊一聊Redis 的内存淘汰(驱逐)策略
大家好,我是 V 哥。粉丝小 A 面试阿里时被问到 Redis 的内存淘汰策略问题,特此整理了一份详细笔记供参考。Redis 的内存淘汰策略决定了在内存达到上限时如何移除数据。希望这份笔记对你有所帮助!欢迎关注“威哥爱编程”,一起学习与成长。
|
1月前
|
缓存 NoSQL 算法
面试题:Redis如何实现分布式锁!
面试题:Redis如何实现分布式锁!