Redis 属于单线程还是多线程?不同的版本有什么区别?

Redis 是普及率最高的技术之一,同时也是面试中必问的一个技术模块,所以从今天开始我们将从最热门的 Redis 面试题入手,更加深入的学习和了解一下 Redis。

我们本文的面试题是 Redis 属于单线程还是多线程?

典型回答

本文的问题在不同的 Redis 版本下答案是不同的,在 Redis 4.0 之前,Redis 是单线程运行的,但单线程并不意味着性能低,类似单线程的程序还有 Nginx 和 NodeJs 他们都是单线程程序,但是效率并不低。 Redis 的 FAQ(Frequently Asked Questions,常见问题)也回到过这个问题,具体内容如下:

Redis is single threaded. How can I exploit multiple CPU / cores?

It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

详情请见:https://redis.io/topics/faq

他的大体意思是说 Redis 是基于内存操作的,因此他的瓶颈可能是机器的内存或者网络带宽而并非 CPU,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了,况且使用多线程比较麻烦。但是在 Redis 4.0 中开始支持多线程了,例如后台删除等功能。

简单来说 Redis 之所以在 4.0 之前一直采用单线程的模式是因为以下三个原因:

  • 使用单线程模型是 Redis 的开发和维护更简单,因为单线程模型方便开发和调试;
  • 即使使用单线程模型也并发的处理多客户端的请求,主要使用的是多路复用(详见本文下半部分);
  • 对于 Redis 系统来说,主要的性能瓶颈是内存或者网络带宽而并非 CPU。

Redis 在 4.0 中引入了惰性删除(也可以叫异步删除),意思就是说我们可以使用异步的方式对 Redis 中的数据进行删除操作了,例如 unlink key / flushdb async / flushall async 等命令,他们的执行示例如下:

> unlink key # 后台删除某个 key
> OK # 执行成功
> flushall async # 清空所有数据
> OK # 执行成功

这样处理的好处是不会导致 Redis 主线程卡顿,会把这些删除操作交给后台线程来执行。

小贴士:通常情况下使用 del 指令可以很快的删除数据,而当被删除的 key 是一个非常大的对象时,例如时包含了成千上万个元素的 hash 集合时,那么 del 指令就会造成 Redis 主线程卡顿,因此使用惰性删除可以有效的避免 Redis 卡顿的问题。

考点分析

关于 Redis 线程模型的问题(单线程或多线程)几乎是 Redis 必问的问题之一,但能回答好的人却寥寥无几,大部分的人只能回到上来 Redis 是单线程的以及说出来单线程的众多好处,但对于 Redis 4.0 和 Redis 6.0 中,尤其是 Redis 6.0 中的多线程能回答上来的人少之又少,和这个知识点相关的面试题还有以下这些。

  • Redis 主线程既然是单线程的,为什么还这么快?
  • 介绍一下 Redis 中的多路复用?
  • 介绍一下 Redis 6.0 中的多线程?

知识扩展

1.Redis 为什么这么快?

我们知道 Redis 4.0 之前是单线程的,那既然是单线程为什么还能这么快?

Redis 速度比较快的原因有以下几点:

  • 基于内存操作:Redis 的所有数据都存在内存中,因此所有的运算都是内存级别的,所以他的性能比较高;
  • 数据结构简单:Redis 的数据结构比较简单,是为 Redis 专门设计的,而这些简单的数据结构的查找和操作的时间复杂度都是 O(1),因此性能比较高;
  • 多路复用和非阻塞 I/O:Redis 使用 I/O 多路复用功能来监听多个 socket 连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,同时也避免了 I/O 阻塞操作,从而大大提高了 Redis 的性能;
  • 避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生。

官方使用基准测试的结果是,单线程的 Redis 吞吐量可以达到 10W/每秒,如下图所示: image.png

2.I/O 多路复用

套接字的读写方法默认情况下是阻塞的,例如当调用读取操作 read 方法时,缓冲区没有任何数据,那么这个线程就会阻塞卡在这里,直到缓冲区有数据或者是连接被关闭时,read 方法才可以返回,线程才可以继续处理其他业务。

但这样显然降低了程序的整体执行效率,而 Redis 使用的就是非阻塞的 I/O,这就意味着 I/O 的读写流程不再是阻塞的,读写方法都是瞬间完成并返回的,也就是他会采用能读多少读多少能写多少写多少的策略来执行 I/O 操作,这显然更符合我们对性能的追求。

但这种非阻塞的 I/O 依然存在一个问题,那就是当我们执行读取数据操作时,有可能只读取了一部分数据,同样写入数据也是这种情况,当缓存区满了之后我们的数据还没写完,剩余的数据何时写何时读就成了一个问题。

而 I/O 多路复用就是解决上面的这个问题的,使用 I/O 多路复用最简单的实现方式就是使用 select 函数,此函数为操作系统提供给用户程序的 API 接口,是用于监控多个文件描述符的可读和可写情况的,这样就可以监控到文件描述符的读写事件了,当监控到相应的事件之后就可以通知线程处理相应的业务了,这样就保证了 Redis 读写功能的正常执行了。

I/O 多路复用执行流程如下图所示: image.png

小贴士:现在的操作系统已经很少使用 select 函数了,改为调用 epoll(linux)和 kqueue(MacOS)等函数了,因为 select 函数在文件描述符特别多时性能非常的差。

3.Redis 6.0 多线程

Redis 单线程的优点很明显,不但降低了 Redis 内部的实现复杂性,也让所有操作都可以在无锁的情况下进行操作,并且不存在死锁和线程切换带来的性能和时间上的消耗,但缺点也很明显,单线程的机制导致 Redis 的 QPS(Query Per Second,每秒查询率)很难得到有效的提高。

Redis 4.0 版本中虽然引入了多线程,但此版本中的多线程只能用于大数据量的异步删除,然而对于非删除操作的意义并不是很大。

如果我们使用多线程就可以分摊 Redis 同步读写 I/O 的压力,以及充分的利用多核 CPU 的资源,并且可以有效的提升 Redis 的 QPS。在 Redis 中虽然使用了 I/O 多路复用,并且是基于非阻塞 I/O 进行操作的,但 I/O 的读和写本身是堵塞的,比如当 socket 中有数据时,Redis 会通过调用先将数据从内核态空间拷贝到用户态空间,再交给 Redis 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。

因此在 Redis 6.0 中新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,但 Redis 的命令依旧是由主线程串行执行的。

需要注意的是 Redis 6.0 默认是禁用多线程的,可以通过修改 Redis 的配置文件 redis.conf 中的 io-threads-do-reads 等于 true 来开启多线程,完整配置为 io-threads-do-reads true,除此之外我们还需要设置线程的数量才能正确的开启多线程的功能,同样是修改 Redis 的配置,例如设置 io-threads 4 表示开启 4 个线程。

小贴士:关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3,如果为 8 核 CPU 建议线程数设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。

关于 Redis 的性能,Redis 作者 antirez 在 RedisConf 2019 分享时曾提到,Redis 6 引入的多线程 I/O 特性对性能提升至少是一倍以上。国内也有人在阿里云使用 4 个线程的 Redis 版本和单线程的 Redis 进行比较测试,发现测试的结果和 antirez 给出的结论基本吻合,性能基本可以提高一倍。

总结

本文我们介绍了 Redis 在 4.0 之前单线程依然很快的原因:基于内存操作、数据结构简单、多路复用和非阻塞 I/O、避免了不必要的线程上下文切换,在 Redis 4.0 中已经添加了多线程的支持,主要体现在大数据的异步删除功能上,例如 unlink key、flushdb async、flushall async 等,Redis 6.0 新增了多线程 I/O 的读写并发能力,用于更好的提高 Redis 的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英语笔记:词组句子:0906

income 收入 mostly 大部门 particularly 尤其、特别 larger drops 更大幅度的下降 thanks to 由于 liable to 易于、可能 in terms of 在……方面 in view of 鉴于、考虑到 over time 随着时间过去 against time 争分夺秒 on time 准时 behind time 误期 widening…

【原创】STM32下波特率计算详解

波特率的计算STM32下的波特率和串口外设时钟息息相关,USART 1的时钟来源于APB2,USART 2-5的时钟来源于APB1。在STM32中,有个波特率寄存器USART_BRR,如下: STM32串口波特率通过USART_BRR进行设置,STM32的波特…

java对数组进行排序_用Java对数组进行排序所需的最少交换

java对数组进行排序Problem: 问题: In this problem, we would have an unordered array with consecutive distinct natural numbers [1,2,3,..n], where n is the size of the array. We have to find the minimum number of swaps required to sort the array i…

如何实现查询附近的人?

查询附近的人或者是附近的商家是一个实用且常用的功能,比如微信中“附近的人”或是美团外卖中“附近商家”等,如下图所示: 那它是如何实现的呢?我们本文就一起来看。 我们本文的面试题是,使用 Redis 如何实现查询附近的人? 典型回答 在说如何实现地理位置查询之前,首…

英语笔记:词组句子:0712

Define sth as … 定义为…… Tie sth to sth 制约 Derive from源自于 Descend from 从……承袭下来 Divide from 分开、隔开 Distinguished from 区别于 Related to 与……有联系 Refer to 查阅、涉及 Attach to 附在……上、系在……上 Associate with 与……有联系…

第五六七章(PTA复习)

第五六七章图形界面线程IO图形界面 主要的布局管理器类包括流布局(FlowLayout)、边界布局(BorderLayout)、网格布局(GridLayout)、卡 片 布 局 (CardLayout) 、 网 格 包 布 局(CardBagLayout) 线程 答案:B IO

Eclipse jetty和plugin 的结合使用

Jetty做为一个轻量级的J2EE Web application server,它不仅小巧,而且性能也比较稳定,效率也挺高,现在也越来越得到广泛的应用。特别是eclipse平台集成了Jetty Plugin后,更是对RCP整合Web Server开发提供了极大的方便。…

python布尔运算符_Python中布尔的逻辑和按位NOT运算符

python布尔运算符In python, not is used for Logical NOT operator, and ~ is used for Bitwise NOT. Here, we will see their usages and implementation in Python. 在python中, not用于逻辑NOT运算符,而〜用于按位NOT。 在这里,我们将看…

英语笔记:作文:What elective to choose

What elective to choose 选修课的选择 Nowadays, thereexist a wide range of selective courses in college which are ready for theundergraduates to choose from. These courses make the students’ college lifemore colorful. However, as for the purpose of choosi…

Redis 有哪些数据类型?

Redis 的数据类型可谓是 Redis 的精华所在,同样的数据类型,例如字符串存储不同的值对应的实际存储结构也是不同,当你存储的 int 值是实际的存储结构也是 int,如果是短字符串(小于 44 字节)实际存储的结构为…

导出/入数据库

导出/入数据库1、以SQL文件的方式1.1导出1.2 导入2、以mdf和ldf数据库文件的方式2.1导出2.1.1 脱机2.1.2 到数据库的数据路径,拷贝出mdf,ldf文件2.1.3 将原数据库设置为online状态即可正常使用2.2导入数据库(切记导入之前要先将控制权限打开)…

arm中clz指令_JavaScript中带有示例的Math.clz32()方法

arm中clz指令JavaScript | Math.clz32()方法 (JavaScript | Math.clz32() Method) Math.clz32() is a function in math library of JavaScript that is used to find the number of leading zeroes in the 32-bit representation of the number. The method will return the n…

Oracle 创建用户 scott 例

在OracleXE中创建scott用户1、打开SQL*Plus,以 sys用户登录数据库 connect / as sysdba2、依次执行下面命令 --DROP USER scott CASCADE; CREATE USER scott IDENTIFIED BY tiger; GRANT connect,resource TO scott; GRANT CREATE DATABASE LINK, CREATE MATERIALIZ…

第八章Transact-SQL程序设计

第八章Transact-SQL程序设计8.1_变量8.1.1_局部变量8.1.2_全局变量8.2_流程控制语句8.2.1_IF...ELSE语句8.2.2_while循环语句8.1_变量 8.1.1_局部变量 局部变量的声明定义: Declare Variable_name Datatype[, Variable_name Datatype]…--举例: decla…

Redis 如何处理已经过期的数据?

上一篇我们讲了 Redis 内存用完之后的内存淘汰策略,它主要是用来出来异常情况下的数据清理,而本文讲的是 Redis 的键值过期之后的数据处理,讲的是正常情况下的数据清理,但面试者常常会把两个概念搞混,以至于和期望的工作失之交臂。我们本文的职责之一就是帮读者朋友搞清楚…

如何删除多余系统引导项

我们很多人都装过双系统,但是有时候装的当中却不想装了或者装不成功,生成的多余系统引导项怎么删除呢?下面分享下我的经验:win7(XP)下如何删除多余的系统引导项。关键词:删除多余系统引导项&…

动态规划编程面试_面试的前25大动态编程问题

动态规划编程面试Dynamic programming is one of the most asked paradigms in any product-based company interviews. You can expect DP in online assessments also if you are in touch with any product-based company. For beginner its, of course, a Tough nut to cra…

第九章存储过程

第九章存储过程9.1_游标的使用9.1.1_游标简介及使用流程9.1.2_游标的声明9.1.3_使用游标读取数据9.1.4_举例说明9.2_存储过程9.2.1_存储过程简介9.2.2_存储过程定义及执行9.2.3_重写存储过程9.2.6_删除存储过程9.2.5_举例说明9.1_游标的使用 9.1.1_游标简介及使用流程 使用游…

Oracle笔记:用户、权限及exp/imp数据

--模式(方案)逻辑概念:一个数据对象的集合,每一个用户--都有一个与之同名的模式,用于存放此用户名下的所有数据对象。select * from user_objectsselect * from dba_users;--创建用户1、给用户创建自己的数据表空间cre…

Redis 内存用完会怎样?

在某些极端情况下,软件为了能正常运行会做一些保护性的措施,比如运行内存超过最大值之后的处理,以及键值过期之后的处理等,都属于此类问题,而专业而全面的回答这些问题恰好是一个工程师所具备的优秀品质。 我们本文的面试题是 Redis 内存用完之后会怎么? 典型回答 Red…