| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> Redis面试题总结(2022版) -> 正文阅读 |
|
[大数据]Redis面试题总结(2022版) |
本文是 Java 面试总结系列的第二篇文章,该专栏将整理和梳理笔者作为 Java 后端程序猿在日常工作以及面试中遇到的实际问题,通过这些问题的系统学习,也帮助笔者顺利拿到阿里、字节、华为、快手等Offer。由于笔者能力有限,其中大多答案来自巨人的肩膀并引以出处。同时若有错误或疏忽还望各位大佬们不吝指出…
一、基础常见考点Redis 为何这么快?
ref 关于Redis处理高并发 缓存三大问题以及解决方案?
1)缓存空值 2)key 值校验,如布隆筛选器 ref 分布式布隆过滤器(Bloom Filter)详解(初版)
1)互斥锁 2)热点数据永不过期 3)熔断降级
1)热点数据不过期 2)随机分散过期时间 ref 【面试】redis缓存穿透、缓存击穿、缓存雪崩区别和解决方案 缓存数据库双写不一致问题?先删后写还是先写后删?
产生脏数据的概率较大(若出现脏数据,则意味着再不更新的情况下,查询得到的数据均为旧的数据)。 比如两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。
产生脏数据的概率较小,但是会出现一致性的问题;若更新操作的时候,同时进行查询操作并命中,则查询得到的数据是旧的数据。但是不会影响后面的查询。 解决方案 1)缓存设置过期时间,实现最终一致性; 2)Cannel 等中间件监听 binlog 进行异步更新。 ref 更新数据时,是先删除缓存再更新DB,还是先更新DB再删除缓存? 如何保证 Redis 的高并发?Redis 通过主从加集群架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发。 ref 如何保证Redis的高并发 Redis 如何保证原子性?常用命令Redis 有哪些常用操作?
ref redis常用命令大全 数据结构Redis 有哪些数据结构?
有序集合 Zset 的底层实现?zset 是 Redis 中一个非常重要的数据结构,其底层是基于跳表(skip list) 实现的。 跳表是一种随机化的数据结构,基于并联的链表,实现简单,插入、删除、查找的复杂度均为 O(logN)。简单说来跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳表能够提供 O(logN) 的时间复杂度。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jJeOB8nN-1644857707745)(https://upload-images.jianshu.io/upload_images/4286978-1c82913df845e4df.png)] 跳表为了避免每次插入或删除带来的额外操作,不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是为每个节点随机出一个层数(level)。而且新插入一个节点不会影响其它节点的层数。因此,插入操作只需要修改插入节点前后的指针,而不需要对很多节点都进行调整。 ref Redis内部数据结构详解(6)——skiplist Zset 为何不使用红黑树等平衡树?1)跳跃表范围查询比平衡树操作简单。 因为平衡树在查询到最小值的时还需要采用中序遍历去查询最大值。 而跳表只需要在找到最小值后,对第一层的链表遍历即可。 2)平衡树的删除和插入需要对子树进行相应的调整,而跳表只需要修改相邻的节点即可。 3)跳表和平衡树的查询操作都是O(logN)的时间复杂度。 4)从整体上来看,跳表算法实现的难度要低于平衡树。 ref redis中的Zset原理 什么是简单动态字符串 SDS?Redis 是基于 C 语言写的,但并未直接使用 C 语言中的字符串,而是自己构建了一种新的字符串数据结构—— Simple Dynamic String。
从上面的定义我们不妨看出,SDS 相比原生字符串可以:
ref Redis详解(四)------ redis的底层数据结构 数据存储Redis 的数据过期策略是什么?在回答词问题之前,首先需要回答另一个问题,就是如何设置 Redis 中数据的过期时间? 1) 除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠 常见的过期策略
在设置 key 的过期时间的同时,为该 key 创建一个定时器,让定时器在 key 的过期时间来临时,对 key 进行删除。
每隔一段时间执行一次删除(在 redis.conf 配置文件设置,1s 刷新的频率)过期 key 操作。 Redis采用的过期策略Redis 采用了惰性删除+定期删除的方式处理过期数据。
1)在进行get或setnx等操作时,先检查key是否过期; 2)若过期,删除key,然后执行相应操作; 3)若没过期,直接执行相应操作。
其核心是对指定个数个库的每一个库随机删除小于等于指定个数个过期 key: 1)历每个数据库(就是 redis.conf 中配置的 “database” 数量,默认为16); 2)检查当前库中的指定个数个 key (默认是每个库检查 20 个,相当于该循环执行 20 次): 2.1)如果当前库中没有一个 key 设置了过期时间,直接执行下一个库的遍历; 2.2)随机获取一个设置了过期时间的 key,检查是否过期,如果过期则删除; 2.3)判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。 持久化文件对过期策略的处理?过期 key 是不会写入 RDB 和 AOF 文件,同时数据恢复时也会做过期验证。 Redis 有哪些内存淘汰机制?Redis 作为一个内存数据库,在内存空间不足的时候,为了保证命中率,就会和我们操作系统中的页面置换算法类似,选择一定的数据淘汰策略。
1)volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。 2)volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。 3)volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。 4)volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
5)allkeys-lru:从数据集中挑选最近最少使用的数据淘汰 6)allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。 7)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
8)no-enviction(驱逐):禁止驱逐数据,这也是默认策略。 意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用 no-enviction 策略可以保证数据不被丢失。 PS:在 redis.config 文件中,我们可以设置 ref Redis5.0数据淘汰策略详解 Redis 有哪些持久化机制?RDB 机制RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式。也就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb。 RDB 支持 同步(save 命令)、后台异步(bgsave)以及自动配置三种方式触发。 优点
缺点 AOF 机制全量备份总是耗时的,有时候我们提供一种更加高效的方式 AOF,其工作机制更加简单:会将每一个收到的写命令追加到文件中。 随着时间推移,AOF 持久化文件也会变的越来越大。为了解决此问题,Redis 提供了 AOF 也有三种触发方式:1)每修改同步 always 2)每秒同步 everysec 3)不同no:从不同步。 优点
缺点
RDB 和 AOF 该如何选择?通过前面章节我们已经明白了两种机制的原理,接下来就该结合具体需求进行机制的选择,通常情况我们是二者结合使用的。 Pipeline什么是 Redis 的 Pipeline?原生批命令 (mset, mget) 与 Pipeline 区别?1)原生批命令是原子性,pipeline是非原子性 2)原生批命令一命令多个 key, 但 pipeline 支持多命令(存在事务),非原子性 3)原生批命令是服务端实现,而 pipeline 需要服务端与客户端共同完成 多线程讲讲 Redis 6 中的多线程?二、集群模式Redis 集群搭建有几种模式?
和 MySQL 需要主从复制的原因一样,Redis 虽然读写速度非常快,但是也会产生性能瓶颈,特别是在读压力上,为了分担压力,Redis 支持主从复制。Redis 的主从结构一主一从,一主多从或级联结构,复制类型可以根据是否是全量而分为全量同步和增量同步。
在主从复制实现之后,如果想对 master 进行监控,Redis 提供了一种哨兵机制,哨兵的含义就是监控 Redis 系统的运行状态,通过投票机制,从 slave 中选举出新的 master 以保证集群正常运行。 还可以启用多个哨兵进行监控以保证集群足够稳健,这种情况下,哨兵不仅监控主从服务,哨兵之间也会相互监控。
ref Redis集群搭建的三种方式 Redis 主从复制的实现?主从复制可以根据需要分为全量同步的增量同步两种方式。 全量同步Redis 全量复制一般发生在 slave 的初始阶段,这时 slave 需要将 master 上的数据都复制一份,具体步骤如下: 1)slave 连接 master,发送 SYNC 命令; 2)master 接到 SYNC 命令后执行 BGSAVE 命令生产 RDB 文件,并使用缓冲区记录此后执行的所有写命令; 3)master 执行完 BGSAVE 后,向所有的 slave 发送快照文件,并在发送过程中继续记录执行的写命令; 4)slave 收到快照后,丢弃所有的旧数据,载入收到的数据; 5)master 快照发送完成后就会开始向 slave 发送缓冲区的写命令; 6)slave 完成对快照的载入,并开始接受命令请求,执行来自 master 缓冲区的写命令; 7)slave 完成上面的数据初始化后就可以开始接受用户的读请求了。 增量同步增量复制实际上就是在 slave 初始化完成后开始正常工作时 master 发生写操作同步到 slave 的过程。增量复制的过程主要是 master 每执行一个写命令就会向 slave 发送相同的写命令,slave 接受并执行写命令,从而保持主从一致。 ref Redis主从复制原理总结 Redis 的主从同步策略?主从同步刚连接的时候进行全量同步,全量同步结束后开始增量同步。 如果有需要,slave 在任何时候都可以发起全量同步,其主要策略就是无论如何首先会尝试进行增量同步,如果失败则会要求 slave 进行全量同步,之后再进行增量同步。 注意:如果多个 slave 同时断线需要重启的时候,因为只要 slave 启动,就会和 master 建立连接发送SYNC请求和主机全量同步,如果多个同时发送 SYNC 请求,可能导致 master IO 突增而发送宕机。所以我们要避免多个 slave 同时恢复重启的情况。 哨兵模式的原理?哨兵主要用于管理多个 Redis 服务器,主要有以下三个任务:监控、提醒以及故障转移。 每个哨兵会向其它哨兵、master、slave 定时发送消息,以确认对方是否还存活。如果发现对方在配置的指定时间内未回应,则暂时认为对方已挂。若“哨兵群”中的多数 sentinel 都报告某一 master 没响应,系统才认为该 master “彻底死亡”,通过一定的 vote 算法从剩下的 slave 节点中选一台提升为 master,然后自动修改相关配置。 哨兵模式故障迁移流程?1)首先是从主服务器的从服务器中选出一个从服务器作为新的主服务器。 选点的依据依次是: 网络连接正常 -> 5 秒内回复过 INFO 命令 -> 10*down-after-milliseconds 内与主连接过的 -> 从服务器优先级 -> 复制偏移量 -> 运行id较小的。 2)选出之后通过 3)然后再通过 缺点
Cluster 模式的原理?其实现原理就是一致性 Hash。Redis Cluster 中有一个 16384 长度的槽的概念,他们的编号为 0、1、2、3 …… 16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster 中的每个 Master 节点都会负责一部分的槽,当有某个 key 被映射到某个 Master 负责的槽,那么这个 Master 负责为这个 key 提供服务。至于哪个 Master 节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在 Redis Cluster 中,只有 Master 才拥有槽的所有权,如果是某个 Master 的 slave,这个slave只负责槽的使用,但是没有所有权。 什么是一致性 Hash 以及解决什么问题?一致性 hash 其实是普通 hash 算法的改良版,其 hash 计算方法没有变化,但是 hash 空间发生了变化,由原来的线性的变成了环。 缓存 key 通过 hash 计算之后得到在 hash 环中的位置,然后顺时针方向找到第一个节点,这个节点就是存放 key 的节点。 由此可见,一致性 hash 主要是为了解决普通 hash 中扩容和宕机的问题。 同时还可以通过虚拟节点来解决数据倾斜的问题:就是在节点稀疏的 hash 环上对物理节点虚拟出一部分虚拟节点,key 会打到虚拟节点上面,而虚拟节点上的 key 实际也是映射到物理节点上的,这样就避免了数据倾斜导致单节点压力过大导致节点雪崩的问题。 ref 什么是一致性hash? Cluster 的分片机制?为了使得集群能够水平扩展,首要解决的问题就是如何将整个数据集按照一定的规则分配到多个节点上。对于客户端请求的 key,根据公式 Redis 集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容。可以说,槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。 集群扩容当一个 Redis 新节点运行并加入现有集群后,我们需要为其迁移槽和数据。首先要为新节点指定槽的迁移计划,确保迁移后每个节点负责相似数量的槽,从而保证这些节点的数据均匀。 1)首先启动一个 Redis 节点,记为 M4。 集群收缩收缩节点就是将 Redis 节点下线,整个流程需要如下操作流程。 1)首先需要确认下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性。 客户端如何路由?既然 Redis 集群中的数据是分片存储的,那我们该如何知道某个 key 存在哪个节点上呢?即我们需要一个查询路由,该路由根据给定的 key,返回存储该键值的机器地址。 常规的实现方式便是采用如下图所示的代理方案,即采用一个中央节点(比如HDFS中的NameNode)来管理所有的元数据,但是这样的方案带来的最大问题就是代理节点很容易成为访问的瓶颈,当读写并发量高的时候,代理节点会严重的拖慢整个系统的性能。 Redis 并没有选择使用代理,而是客户端直接连接每个节点。Redis 的每个节点中都存储着整个集群的状态,集群状态中一个重要的信息就是每个桶的负责节点。在具体的实现中,Redis 用一个大小固定为 CLUSTER_SLOTS 的 clusterNode 数组 slots 来保存每个桶的负责节点。
在集群模式下,Redis 接收任何键相关命令时首先计算键对应的桶编号,再根据桶找出所对应的节点,如果节点是自身,则处理键命令;否则回复 ref Redis集群详解(上) 为什么是 163834 个槽位?从前面的 Cluster 集群原理我们已经了解到集群中的所有节点在握手成功后悔定期发送 ping/pong 消息,交换数据信息。 先来了解一下消息体传递了哪些数据: 上图展示的消息体结构无外乎是一些节点标识,IP,端口号,发送时间等,但需要注意一下标红的 myslots 的 char 数组,长度为 16383/8,这其实是一个 bitmap,每一个位代表一个槽,如果该位为1,表示这个槽是属于这个节点的。
至于这个消息体有多大?显然最占空间的就是 myslots 数组:
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。
集群的故障发现与迁移?故障发现当集群内某个节点出现问题时,需要通过一种健壮的方式保证识别出节点是否发生了故障。Redis 集群内节点通过
集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节点会认为接收节点存在故障,把接收节点标记为主观下线(PFail)状态。
Redis 集群对于节点最终是否故障判断非常严谨,只有一个节点认为主观下线并不能准确判断是否故障。当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播,通过Gossip消息传播,集群内节点不断收集到故障节点的下线报告。当半数以上持有槽的主节点都标记某个节点是主观下线时。触发客观下线流程。 客观下线流程故障恢复故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它,从而保证集群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主节点进入客观下线时,将会触发故障恢复流程。 三、场景如何设置 Key 当天失效?
如何实现分布式锁?
Redison 底层原理: RedLock 加锁步骤: 1)按顺序向集群中所有 master 节点请求加锁; 2)根据设置的超时时间来判断,是不是要跳过该 master 节点; 3)如果大于等于半数节点( N/2+1 )加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦; 4)如果获取锁失败,解锁! 如何实现排行榜功能?活动运营中经常会有这样的需求:1)Value 值排序 2)Value 相同按时间排序。
同时为了实现先到先得,zset 中的 score 并不能仅仅是排序的 Value 值,还需加入时间戳因子: 由于时间戳相同一个月的时间内,头三位是相等的,即我们还可以进一步压缩时间位数以提高排序值的精度。 ref redis zset做排行榜 四、优化大 key 优化为什么要优化大 keyref redis大key优化 热点 key 优化如何优化热点 key?热 key 带来的问题:请求到的分片过于集中,超过单台 Server 的性能极限。 解决方案: 1)服务端缓存:即将热点数据缓存至服务端的内存中; 2)备份热点Key:即将热点Key+随机数,随机分配至 Redis 其它节点中。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 11:34:12- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |