redis属于单线程还是多线程

57次阅读
没有评论

共计 3514 个字符,预计需要花费 9 分钟才能阅读完成。

自动写代码机器人,免费开通

丸趣 TV 小编给大家分享一下 redis 属于单线程还是多线程,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

Redis4.0 之前是单线程运行的;Redis4.0 后开始支持多线程。Redis4.0 之前使用单线程的原因:1、单线程模式方便开发和调试;2、Redis 内部使用了基于 epoll 的多路复用;3、Redis 主要的性能瓶颈是内存或网络带宽。

不同版本的 Redis 是不同的,在 Redis4.0 之前,Redis 是单线程运行的,但单线程并不代表效率低,像 Nginx、Nodejs 也是单线程程序,但是它们的效率并不低。

原因是 Redis 是基于内存的,它的瓶颈在于机器的内存、网络带宽,而不是 CPU,在 CPU 还没达到瓶颈时机器内存可能就满了、或者带宽达到瓶颈了。因此 CPU 不是主要原因,那么自然就采用单线程了,况且使用多线程比较麻烦。

但是在 Redis4.0 的时候,已经开始支持多线程了,比如后台删除等功能。

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

使用单线程模式的 Redis,其开发和维护更简单,因为单线程模式方便开发和调试。

即使使用单线程模型也能够并发地处理多客户端的请求,主要是因为 Redis 内部使用了基于 epoll 的多路复用。

对于 Redis 来说,主要的性能瓶颈是内存或网络带宽,而非 CPU。

但 Redis 在 4.0 以及之后的版本中引入了惰性删除(也叫异步删除),意思就是我们可以使用异步的方式对 Redis 中的数据进行删除操作,例如:

unlink key:和 del key 类似,删除指定的 key,若 key 不存在则 key 被跳过。但是 del 会产生阻塞,而 unlink 命令会在另一个线程中回收内存,即它是非阻塞的【http://www.redis.cn/commands/unlink.html】;

flushdb async:删除当前数据库的所有数据【http://www.redis.cn/commands/flushdb.html】;

flushall async:删除所有库中的数据【http://www.redis.cn/commands/flushall.html】。

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

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

考点分析:

关于 Redis 线程模型的问题(单线程或多线程)几乎是 Redis 必问的问题之一,但回答好的人却不多,大部分只能回答上来 Redis 是单线程的以及说出来单线程的众多好处,但对于 Redis4.0 和 Redis6.0 中,尤其是 Redis6.0 中多线程的特点,能够准确回答上来的人非常少。关于单线程和多线程的相关知识,还有以下面试题。

1.Redis 主线程既然是单线程,为什么还这个快?

2. 介绍一下 Redis 中的 IO 多路复用?

3. 介绍一下 Redis6.0 中的多线程?

1.Redis 为什么这么快?

原因有以下几点:

a. 基于内存操作:Redis 的所有数据都存在内存中,因此所有的运算都是内存级别的,所以它的性能比较高。

b. 数据结构简单:Redis 的数据结构比较简单,是为 Redis 专门设计的,而这些简单的数据结构的查找和操作的时间复杂度都是 O(1)。

c. 多路复用和非阻塞 IO:Redis 使用 IO 多路复用功能来监听多个 socket 连接的客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了 IO 阻塞操作,从而大大提高了 Redis 的性能。

d. 避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生。

官方使用的基准测试结果表明,单线程的 Redis 可以达到 10W/ S 的吞吐量。

2.IO 多路复用是什么?

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

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

但这种非阻塞的 IO 也面临一个问题,那就是当我们执行读取操作时,有可能只读取了一部分数据;同理写数据也是这种情况,当缓冲区满了,而我们的数据还没有写完,那么生效的数据何时写就成了一个问题。

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

【不过现在的操作系统已经基本上不适用 select 函数了,改为调用 epoll 函数(Linux)了,macOS 则是使用 Kqueue(继承与 Unix),因为 select 函数在文件描述符非常多的时候性能非常差。】

3.Redis6.0 中的多线程?

Redis 单线程的优点非常,不但降低了 Redis 内部实现的负责性,也让所有操作都可以在无锁的情况下进行,并且不存在死锁和线程切换带来的性能以及时间上的消耗;但是其缺点也很明显,单线程机制导致 Redis 的 QPS(Query Per Second,每秒查询数)很难得到有效的提高(虽然够快了,但人毕竟还是要有更高的追求的)。

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

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

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

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

【关于线程数的设置,官方的建议是如果为 4 核 CPU,那么设置线程数为 2 或 3;如果为 8 核 CPU,那么设置线程数为 6. 总之线程数一定要小于机器的 CPU 核数,线程数并不是越大越好。】

关于 Redis 的性能,Redis 的作者在 2019 年的 RedisConf 大会上提到,Redis6.0 引入的多线程 IO 特性对性能的提升至少是一倍以上。国人也有在阿里云使用 4 个线程的 Redis 版本和单线程的 Redis 进行比较测试,发现测试结果和 Redis 作者说的一致,性能基本可以提高一倍。

总结:

本文介绍了 Redis 在 4.0 之前单线程依然快的原因:基于内存操作、数据结构简单、IO 多路复用和非阻塞 IO、避免了不必要的线程上下文切换。并且在 Redis4.0 开始支持多线程,主要体现在大数据的异步删除方面,例如:unlink key、flushdb async、flushall async 等。而 Redis6.0 的多线程则增加了对 IO 读写的并发能力,用于更好的提升 Redis 的性能。

以上是“redis 属于单线程还是多线程”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注丸趣 TV 行业资讯频道!

向 AI 问一下细节

丸趣 TV 网 – 提供最优质的资源集合!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-12-18发表,共计3514字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)