目录
一、Redis为什么这么快?
二、Redis为什么被设计成单线程的?
2.1 多线程适用场景
2.2 多线程的弊端
2.3 小结
三、为什么Redis设计成单线程也能这么快?
四、为什么Redis6.0引入了多线程?
Redis 之所以如此快,主要有以下几个方面的原因:
基于内存:Redis 是一种基于内存的数据库,数据存储在内存中,数据的读写速度非常快,因为内存访问速度比硬盘访问速度快得多。
单线程模型:Redis 使用单线程模型,这意味着它的所有操作都是在一个线程内完成的,不需要进行线程切换和上下文切换。这大大提高了 Redis 的运行效率和响应速度。
多路复用 I/O 模型:Redis 在单线程的基础上,采用了 I/O 多路复用技术,实现了单个线程同时处理多个客户端连接的能力,从而提高了 Redis 的并发性能。
高效的数据结构:Redis 提供了多种高效的数据结构,如哈希表、有序集合、列表等,这些数据结构都被实现得非常高效,能够在 O(1) 的时间复杂度内完成数据读写操作,这也是 Redis 能够快速处理数据请求的重要因素之一。
多线程的引入:在 Redis 6.0 中,为了进一步提升 IO 的性能,引入了多线程的机制。采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。
Redis 作为一个成熟的分布式缓存框架,它由很多个模块组成,如网络请求模块、索引模块、存储模块、高可用集群支撑模块、数据操作模块等。
很多人说 Redis 是单线程的,就认为 Redis 中所有模块的操作都是单线程的,其实这是不对的。
我们所说的 Redis 单线程,指的是“其网络 IO 和键值对读写是由一个线程完成的”,也就是说,Redis 中只有网络请求模块和数据操作模块是单线程的。而其他的如持久化存储模块、集群支撑模块等是多线程的。
所以说,Redis 中并不是没有多线程模型的,早在 Redis 4.0 的时候就已经针对部分命令做了多线程化。
那么,为什么网络操作模块和数据存储模块最初并没有使用多线程呢?
这个问题的答案比较简单!因为:“没必要!”
为什么没必要呢?我们先来说一下,什么情况下要使用多线程?
一个计算机程序在执行的过程中,主要需要进行两种操作分别是读写操作和计算操作。
其中读写操作主要是涉及到的就是 I/O 操作,其中包括网络 I/O 和磁盘 I/O。计算操作主要涉及到 CPU。
而多线程的目的,就是通过并发的方式来提升 I/O 的利用率和 CPU 的利用率。
那么,Redis 需不需要通过多线程的方式来提升提升 I/O 的利用率和 CPU 的利用率呢?
首先,我们可以肯定地说,Redis 不需要提升 CPU 利用率,因为 Redis 的操作基本都是基于内存的,CPU 资源根本就不是 Redis 的性能瓶颈。
所以,通过多线程技术来提升 Redis 的 CPU 利用率这一点是完全没必要的。
那么,使用多线程技术来提升 Redis 的 I/O 利用率呢?是不是有必要呢?
Redis 确实是一个 I/O 操作密集的框架,他的数据操作过程中,会有大量的网络 I/O 和磁盘 I/O 的发生。要想提升 Redis 的性能,是一定要提升 Redis 的 I/O 利用率的,这一点毋庸置疑。
但是,提升 I/O 利用率,并不是只有采用多线程技术这一条路可以走!
我们在很多文章中介绍过一些 Java 中的多线程技术,如内存模型、锁、CAS 等,这些都是 Java 中提供的一些在多线程情况下保证线程安全的技术。
线程安全:是编程中的术语,指某个函数、函数库在并发环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。
和 Java 类似,所有支持多线程的编程语言或者框架,都不得不面对的一个问题,那就是如何解决多线程编程模式带来的共享资源的并发控制问题。
虽然,采用多线程可以帮助我们提升 CPU 和 I/O 的利用率,但是多线程带来的并发问题也给这些语言和框架带来了更多的复杂性。而且,多线程模型中,多个线程的互相切换也会带来一定的性能开销。
所以,在提升 I/O 利用率这个方面上,Redis 并没有采用多线程技术,而是选择了多路复用 I/O 技术。
Redis 并没有在网络请求模块和数据操作模块中使用多线程模型,主要是基于以下四个原因:
还是要记住:Redis 并不是完全单线程的,只是有关键的键值对读写是由一个线程完成的。
Redis 的性能很好,除了因为他基于内存、有高效的数据结构等等原因以外,还有一个重要的原因那就是他在单线程中使用多路复用 I/O 技术也能提升 Redis 的 I/O 利用率。
多路复用这个词,相信很多人都不陌生。那么,Redis 的多路复用技术有什么特别的呢?
这里先讲解 Linux 多路复用技术,就是多个进程的 I/O 可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。
也就是说,通过一个线程来处理多个 I/O 流。
I/O 多路复用在 Linux 下包括了三种:select、poll、epoll。抽象来看,它们功能是类似的,但具体细节各有不同。
其实,Redis 的 I/O 多路复用程序的所有功能都是通过包装操作系统的 I/O 多路复用函数库来实现的。每个 I/O 多路复用函数库在 Redis 源码中都有对应的一个单独的文件。
在 Redis 中,每当一个套接字准备好执行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。
一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。
所以,Redis 选择使用多路复用 I/O 技术来提升 I/O 利用率。
而之所以 Redis 能够有这么高的性能,不仅仅和采用多路复用技术和单线程有关,此外还有以下几个原因:
参考我另外一篇文章:为什么Redis6.0引入了多线程?_redis哪个版本开始支持多线程-CSDN博客