2021年最新Redis面试题汇总(1)

目录
  • 1、Redis 是单线程还是多线程?
  • 2、为什么 Redis 是单线程?
  • 3、Redis 为什么使用单进程、单线程也很快
  • 4、Redis 在项目中的使用场景
  • 5、Redis 常见的数据结构
  • 6、Redis 的字符串(SDS)和C语言的字符串区别
  • 7、Sorted Set底层数据结构
  • 8、Sorted Set 为什么同时使用字典和跳跃表?
  • 9、Sorted Set 为什么使用跳跃表,而不是红黑树?
  • 10、Hash 对象底层结构
  • 11、Hash 对象的扩容流程
  • 总结

1、Redis 是单线程还是多线程?

这个问题应该已经看到过无数次了,最近 redis 6 出来之后又被翻出来了。

redis 4.0 之前,redis 是完全单线程的

redis 4.0 时,redis 引入了多线程,但是额外的线程只是用于后台处理,例如:删除对象,核心流程还是完全单线程的。这也是为什么有些人说 4.0 是单线程的,因为他们指的是核心流程是单线程的。

这边的核心流程指的是 redis 正常处理客户端请求的流程,通常包括:接收命令、解析命令、执行命令、返回结果等。

而在最近,redis 6.0 版本又一次引入了多线程概念,与 4.0 不同的是,这次的多线程会涉及到上述的核心流程。

redis 6.0 中,多线程主要用于网络 I/O 阶段,也就是接收命令和写回结果阶段,而在执行命令阶段,还是由单线程串行执行。由于执行时还是串行,因此无需考虑并发安全问题。

值得注意的时,redis 中的多线程组不会同时存在“读”和“写”,这个多线程组只会同时“读”或者同时“写”。

redis 6.0 加入多线程 I/O 之后,处理命令的核心流程如下:

1、当有读事件到来时,主线程将该客户端连接放到全局等待读队列

2、读取数据:1)主线程将等待读队列的客户端连接通过轮询调度算法分配给 I/O 线程处理;2)同时主线程也会自己负责处理一个客户端连接的读事件;3)当主线程处理完该连接的读事件后,会自旋等待所有 I/O 线程处理完毕

3、命令执行:主线程按照事件被加入全局等待读队列的顺序(这边保证了执行顺序是正确的),串行执行客户端命令,然后将客户端连接放到全局等待写队列

4、写回结果:跟等待读队列处理类似,主线程将等待写队列的客户端连接使用轮询调度算法分配给 I/O 线程处理,同时自己也会处理一个,当主线程处理完毕后,会自旋等待所有 I/O 线程处理完毕,最后清空队列。

大致流程图如下:

2、为什么 Redis 是单线程?

在 redis 6.0 之前,redis 的核心操作是单线程的。

因为 redis 是完全基于内存操作的,通常情况下CPU不会是redis的瓶颈,redis 的瓶颈最有可能是机器内存的大小或者网络带宽。

既然CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了,因为如果使用多线程的话会更复杂,同时需要引入上下文切换、加锁等等,会带来额外的性能消耗。

而随着近些年互联网的不断发展,大家对于缓存的性能要求也越来越高了,因此 redis 也开始在逐渐往多线程方向发展。

最近的 6.0 版本就对核心流程引入了多线程,主要用于解决 redis 在网络 I/O 上的性能瓶颈。而对于核心的命令执行阶段,目前还是单线程的。

3、Redis 为什么使用单进程、单线程也很快

主要有以下几点:

1、基于内存的操作

2、使用了 I/O 多路复用模型,select、epoll 等,基于 reactor 模式开发了自己的网络事件处理器

3、单线程可以避免不必要的上下文切换和竞争条件,减少了这方面的性能消耗。

4、以上这三点是 redis 性能高的主要原因,其他的还有一些小优化,例如:对数据结构进行了优化,简单动态字符串、压缩列表等。

4、Redis 在项目中的使用场景

缓存(核心)、分布式锁(set + lua 脚本)、排行榜(zset)、计数(incrby)、消息队列(stream)、地理位置(geo)、访客统计(hyperloglog)等。

5、Redis 常见的数据结构

基础的5种:

  • String:字符串,最基础的数据类型。
  • List:列表。
  • Hash:哈希对象。
  • Set:集合。
  • Sorted Set:有序集合,Set 的基础上加了个分值。

高级的4种:

  • HyperLogLog:通常用于基数统计。使用少量固定大小的内存,来统计集合中唯一元素的数量。统计结果不是精确值,而是一个带有0.81%标准差(standard error)的近似值。所以,HyperLogLog适用于一些对于统计结果精确度要求不是特别高的场景,例如网站的UV统计。
  • Geo:redis 3.2 版本的新特性。可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作:获取2个位置的距离、根据给定地理位置坐标获取指定范围内的地理位置集合。
  • Bitmap:位图。
  • Stream:主要用于消息队列,类似于 kafka,可以认为是 pub/sub 的改进版。提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。

6、Redis 的字符串(SDS)和C语言的字符串区别

7、Sorted Set底层数据结构

Sorted Set(有序集合)当前有两种编码:ziplist、skiplist

ziplist:使用压缩列表实现,当保存的元素长度都小于64字节,同时数量小于128时,使用该编码方式,否则会使用 skiplist。这两个参数可以通过 zset-max-ziplist-entries、zset-max-ziplist-value 来自定义修改。

skiplist:zset实现,一个zset同时包含一个字典(dict)和一个跳跃表(zskiplist)

8、Sorted Set 为什么同时使用字典和跳跃表?

主要是为了提升性能。

单独使用字典:在执行范围型操作,比如 zrank、zrange,字典需要进行排序,至少需要 O(NlogN) 的时间复杂度及额外 O(N) 的内存空间。

单独使用跳跃表:根据成员查找分值操作的复杂度从 O(1) 上升为 O(logN)

9、Sorted Set 为什么使用跳跃表,而不是红黑树?

主要有以下几个原因:

1)跳表的性能和红黑树差不多。

2)跳表更容易实现和调试。

网上有同学说是因为作者不会红黑树,我觉得挺有可能的。

10、Hash 对象底层结构

Hash 对象当前有两种编码:ziplisthashtable

ziplist:使用压缩列表实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的节点推入到压缩列表的表尾,然后再将保存了值的节点推入到压缩列表表尾。

因此:1)保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后;2)先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加的会被放在表尾方向。

hashtable:使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值来保存,跟 java 中的 HashMap 类似。

11、Hash 对象的扩容流程

hash 对象在扩容时使用了一种叫“渐进式 rehash”的方式,步骤如下:

1)计算新表 size、掩码,为新表 ht[1] 分配空间,让字典同时持有 ht[0] 和 ht[1] 两个哈希表。

2)将 rehash 索引计数器变量 rehashidx 的值设置为0,表示 rehash 正式开始。

3)在 rehash 进行期间,每次对字典执行添加、删除、査找、更新操作时,程序除了执行指定的操作以外,还会触发额外的 rehash 操作,在源码中的 _dictRehashStep 方法。

_dictRehashStep:从名字也可以看出来,大意是 rehash 一步,也就是 rehash 一个索引位置。

该方法会从 ht[0] 表的 rehashidx 索引位置上开始向后查找,找到第一个不为空的索引位置,将该索引位置的所有节点 rehash 到 ht[1],当本次 rehash 工作完成之后,将 ht[0] 索引位置为 rehashidx 的节点清空,同时将 rehashidx 属性的值加一。

4)将 rehash 分摊到每个操作上确实是非常妙的方式,但是万一此时服务器比较空闲,一直没有什么操作,难道 redis 要一直持有两个哈希表吗?

答案当然不是的。我们知道,redis 除了文件事件外,还有时间事件,redis 会定期触发时间事件,这些时间事件用于执行一些后台操作,其中就包含 rehash 操作:当 redis 发现有字典正在进行 rehash 操作时,会花费1毫秒的时间,一起帮忙进行 rehash。

5)随着操作的不断执行,最终在某个时间点上,ht[0] 的所有键值对都会被 rehash 至 ht[1],此时 rehash 流程完成,会执行最后的清理工作:释放 ht[0] 的空间、将 ht[0] 指向 ht[1]、重置 ht[1]、重置 rehashidx 的值为 -1。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

2021年最新Redis面试题汇总(2)

2021年最新Redis面试题汇总(3)

2021年最新Redis面试题汇总(4)

(0)

相关推荐

  • 几道常问Redis面试题,你能答对吗?

    目录 1.Redis支持的数据类型? 2.什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么? 3.Redis 有哪些架构模式?讲讲各自的特点 4.使用过Redis分布式锁么,它是怎么实现的? 5.使用过Redis做异步队列么,你是怎么用的?有什么缺点? 6.什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免? 7.Redis常用命令 8.Redis单例.主从模式.sentinel以及集群的配置方式及优缺点对比 9.为什么Redis 单线程却能支撑高并发? 10.Redis常见性

  • Java面试题冲刺第二天--Redis篇

    目录 面试题1:为什么要用 Redis ?业务在哪块儿用到的? 正经回答: 深入追问: 追问1:Redis里有哪些数据类型? 追问2:Redis与Memcached有哪些区别? 追问3:那Redis怎样防止异常数据不丢失的?如何持久化? 面试题2:Redis为啥是单线程的? 正经回答: 深入追问: 追问1:单线程只使用了单核CPU,太浪费,有什么办法发挥多核CPU的性能嘛? 面试题3:聊一下对缓存穿透.缓存击穿.缓存雪崩的理解吧 正经回答: 深入追问: 追问1:那你说一下针对缓存击穿的解决方法

  • 硬核 Redis 高频面试题解析

    目录 1.Redis 是单线程还是多线程? 2.为什么 Redis 是单线程? 3.Redis 为什么使用单进程.单线程也很快 4.Redis 在项目中的使用场景 5.Redis 常见的数据结构 6.Redis 的字符串(SDS)和C语言的字符串区别 7.Sorted Set底层数据结构 8.Sorted Set 为什么同时使用字典和跳跃表? 9.Sorted Set 为什么使用跳跃表,而不是红黑树? 10.Hash 对象底层结构 11.Hash 对象的扩容流程 12.渐进式 rehash 的优

  • Redis分布式锁的实现方式(redis面试题)

    什么是分布式锁? 要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁.进程锁. 线程锁:主要用来给方法.代码块加锁.当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段.线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state). 进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronize

  • 2021年最新Redis面试题汇总(1)

    目录 1.Redis 是单线程还是多线程? 2.为什么 Redis 是单线程? 3.Redis 为什么使用单进程.单线程也很快 4.Redis 在项目中的使用场景 5.Redis 常见的数据结构 6.Redis 的字符串(SDS)和C语言的字符串区别 7.Sorted Set底层数据结构 8.Sorted Set 为什么同时使用字典和跳跃表? 9.Sorted Set 为什么使用跳跃表,而不是红黑树? 10.Hash 对象底层结构 11.Hash 对象的扩容流程 总结 1.Redis 是单线程还

  • 2021年最新Redis面试题汇总(3)

    目录 1.Redis 怎么保证高可用.有哪些集群模式 2.主从复制 ​3.哨兵 1)哨兵故障检测 2)哨兵故障转移流程 4.集群模式 ​5.集群选举 6.如何保证集群在线扩容的安全性?(Redis 集群要增加分片,槽的迁移怎么保证无损) 7.Redis 事务的实现 8.Redis 的 Java 客户端有哪些?官方推荐哪个? 9.Redis 里面有1亿个 key,其中有 10 个 key 是包含 java,如何将它们全部找出来? 10.使用过 Redis 做消息队列么? 11.Redis 和 Me

  • 2021年最新Redis面试题汇总(2)

    目录 1.渐进式 rehash 的优点 2.rehash 流程在数据量大的时候会有什么问题吗(Hash 对象的扩容流程在数据量大的时候会有什么问题吗) 3.Redis 的网络事件处理器(Reactor 模式) 4.Redis 删除过期键的策略(缓存失效策略.数据过期策略) 5.Redis 的内存淘汰(驱逐)策略 6.Redis 的 LRU 算法怎么实现的? 7.Redis 的持久化机制有哪几种,各自的实现原理和优缺点? 1)RDB 2)AOF 8.为什么需要 AOF 重写 9.介绍下 AOF 重

  • 2021年最新Redis面试题汇总(4)

    目录 1.Redis 实现分布式锁 2.Redis 分布式锁过期了,还没处理完怎么办 3.守护线程续命的方案有什么问题吗 4.RedLock 5.使用缓存时,先操作数据库 or 先操作缓存 6.为什么是让缓存失效,而不是更新缓存 7.如何保证数据库和缓存的数据一致性 ​8.缓存穿透 9.布隆过滤器 10.缓存击穿 11.缓存雪崩 总结 1.Redis 实现分布式锁 1)加锁 加锁通常使用 set 命令来实现,伪代码如下: set key value PX milliseconds NX 几个参数

  • 2021最新Android笔试题总结美团Android岗职能要求

    目录 Android开发面试的几部分 1.基础知识 Java部分: Android部分: 数据结构与算法: 计算机基础: 设计模式: 开源项目: 重点项目经历 技术以外的东西 自我驱动和追求 沟通和协作 我的面经总结 Android Java 计算机网络 数据结构及算法 题外话 优秀的战士需要出色的剑才能战斗.同样,在现代IT中,每个编码人员都需要最好的Android开发人员工具来提高他们的技能和效率.在Android应用程序开发这个残酷的竞争行业中,只有优秀的开发人员才能生存下去.您需要向客户

  • GO必知必会的常见面试题汇总

    目录 引言 值类型和引用类型 值类型有哪些? 引用类型有哪些? 值类型和引用类型的区别? 垃圾回收 一图胜千言 堆和栈 栈 堆 切片 比较 比较的详解 深拷贝和浅拷贝 操作对象 区别如下: new和make new 特点 举个例子: 使用技巧 make make函数的函数签名 特点 使用技巧 小结:new与make的区别 go的map实现排序 解决思路 代码实现: 运行结果 逃逸分析 最后,听我说 引言 今年互联网的就业环境真的好糟糕啊,好多朋友被优化. 我们平常在工作中除了撸好代码,跑通项目之

  • Android 面试题汇总

        Android 70道面试题汇总不再愁面试 本文为开发者奉献了70道经典Android面试题加答案--重要知识点几乎都涉及到了,你还等啥,赶紧收藏吧!! 1. 下列哪些语句关于内存回收的说明是正确的? (b) A. 程序员必须创建一个线程来释放内存 B. 内存回收程序负责释放无用内存 C. 内存回收程序允许程序员直接释放内存 D. 内存回收程序可以在指定的时间释放内存对象 2. 下面异常是属于Runtime Exception 的是(abcd)(多选) A.ArithmeticExcep

  • Java常用类String的面试题汇总(java面试题)

    1.比较两个字符串时使用"=="还是equals()方法? 当然是equals方法."=="测试的是两个对象的引用是否相同,而equals()比较的是两个字符串的值是否相等.简单来说,基本数据类型都可以使用==.而引用类型使用==比较不了. 2.如何将字符串转化成int? 使用包装类Integer.Integer.valueOf("2");其他基本数据类型都是类似 3.为什么在Java中存储密码要使用char[],而不使用String. 因为St

  • 关于vue面试题汇总

    vue的底层原理? vue组件之间的通信? JS中判断数据类型的方法有几种? 最常见的判断方法:typeof 判断已知对象类型的方法: instanceof 根据对象的constructor判断: constructor 无敌万能的方法:jquery.type() vue与angular的区别? 1.vue仅仅是mvvm中的view层,只是一个如jquery般的工具库,而不是框架,而angular而是mvvm框架. 2.vue的双向邦定是基于ES5 中的 3.getter/setter来实现的,

随机推荐