为什么说Redis是单线程的?

Redis为何又快又单线程
本文介绍了Redis作为一款高性能内存数据库的特点,探讨了其快速响应的原因,包括内存操作、简单数据结构、单线程模型、多路I/O复用及底层优化机制。此外,文章还解释了Redis选择单线程的原因。

一、前言

近乎所有与Java相关的面试都会问到缓存的问题,基础一点的会问到什么是“二八定律”、什么是“热数据和冷数据” ,复杂一点的会问到缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题,这些看似不常见的概念,都与我们的缓存服务器相关,一般常用的缓存服务器有Redis、Memcached等,而笔者目前最常用的也只有Redis这一种。

如果你在以前面试的时候还没有遇到过面试官问你:为什么说Redis是单线程或者Redis为什么这么快?,那么你看到这篇文章的时候,你应该觉得是一件很幸运的事情!如果你刚好是一位高逼格的面试官,你也可以拿这道题去面试对面“望穿秋水”般的小伙伴,测试一下他的掌握程度。

好啦!步入正题!我们先探讨一下Redis是什么,Redis为什么这么快、然后在探讨一下为什么Redis是单线程的?

二、Redis简介

Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件

它支持多种类型的数据结构,如字符串(Strings),散列(Hash),列表(List),集合(Set),有序集合(Sorted Set或者是ZSet)与范围查询,Bitmaps,Hyperloglogs 和地理空间(Geospatial)索引半径查询。其中常见的数据结构类型有:String、List、Set、Hash、ZSet这5种。

Redis 内置了复制(Replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(Transactions) 和不同级别的磁盘持久化(Persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(High Availability)。

Redis也提供了持久化的选项,这些选项可以让用户将自己的数据保存到磁盘上面进行存储。根据实际情况,可以每隔一定时间将数据集导出到磁盘(快照),或者追加到命令日志中(AOF只追加文件),他会在执行写命令时,将被执行的写命令复制到硬盘里面。您也可以关闭持久化功能,将Redis作为一个高效的网络的缓存数据功能使用。

Redis不使用表,他的数据库不会预定义或者强制去要求用户对Redis存储的不同数据进行关联。

数据库的工作模式按存储方式可分为:硬盘数据库和内存数据库。Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。

1、硬盘数据库的工作模式: 

640

2、内存数据库的工作模式:

640

看完上述的描述,对于一些常见的Redis相关的面试题,是否有所认识了,例如:什么是Redis、Redis常见的数据结构类型有哪些、Redis是如何进行持久化的等。

三、Redis到底有多快

Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcached 差!有兴趣的可以参考官方的基准程序测试:https://redis.io/topics/benchmarks

640

横轴是连接数,纵轴是QPS。此时,这张图反映了一个数量级,希望大家在面试的时候可以正确的描述出来,不要问你的时候,你回答的数量级相差甚远!

四、Redis为什么这么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

以上几点都比较好理解,下边我们针对多路 I/O 复用模型进行简单的探讨:

(1)多路 I/O 复用模型

多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

五、那么为什么Redis是单线程的

我们首先要明白,上边的种种分析,都是为了营造一个Redis很快的氛围!官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。

640

可以参考:https://redis.io/topics/faq

看到这里,你可能会气哭!本以为会有什么重大的技术要点才使得Redis使用单线程就可以这么快,没想到就是一句官方看似糊弄我们的回答!但是,我们已经可以很清楚的解释了为什么Redis这么快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了!

但是,我们使用单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例来完善!

警告1:这里我们一直在强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定是不止一个线程的,这里需要大家明确的注意一下!例如Redis进行持久化的时候会以子进程或者子线程的方式执行(具体是子线程还是子进程待读者深入研究);例如我在测试服武器上查看Redis进程,然后找到该进程下的线程:

640

ps命令的“-T”参数表示显示线程(Show threads, possibly with SPID column.)“SID”栏表示线程ID,而“CMD”栏则显示了线程名称。

警告2:在上图中FAQ中的最后一段,表述了从Redis 4.0版本开始会支持多线程的方式,但是,只是在某一些操作上进行多线程的操作!所以该篇文章在以后的版本中是否还是单线程的方式需要读者考证!

六、扩展

以下也是你应该知道的几种模型,祝你的面试一臂之力!

1、单进程多线程模型:MySQL、Memcached、Oracle(Windows版本);

2、多进程模型:Oracle(Linux版本);

3、Nginx有两类进程,一类称为Master进程(相当于管理进程),另一类称为Worker进程(实际工作进程)。启动方式有两种:

(1)单进程启动:此时系统中仅有一个进程,该进程既充当Master进程的角色,也充当Worker进程的角色。

(2)多进程启动:此时系统有且仅有一个Master进程,至少有一个Worker进程工作。

(3)Master进程主要进行一些全局性的初始化工作和管理Worker的工作;事件处理是在Worker中进行的。

640


(完)


PS:如果觉得我的分享不错,欢迎大家随手点赞、转发。


Java团长

专注于Java干货分享

640

扫描上方二维码获取更多Java干货

<think>嗯,用户的问题是关于Redis单线程特性,具体是为什么被称为单线程以及这种特性的体现。用户还提供了站内的引用内容,我需要整合这些信息给出一个清晰的解释。 用户的历史对话中有关于Java GC的内容,但当前问题转向了Redis,看起来用户对系统设计中的线程模型比较感兴趣。用户可能是个开发者,正在学习Redis的内部机制,或者遇到了性能相关的问题想深入理解。 从引用材料看,Redis单线程是个经典话题,但不同版本有差异。我需要特别注意: - 核心流程的单线程性(网络IO和键值读写) - 多线程辅助任务的存在(持久化、删除等) - 版本演进带来的变化(4.x引入异步删除,6.x引入IO多线程) 用户提供的五个引用中,[2]和[3]最核心,明确指出单线程指的是主流程串行化。[4]强调了版本差异,这点很重要,避免用户混淆不同版本的行为。[5]则解释了设计原因:内存操作、避免上下文切换、配合IO多路复用。 需要特别澄清的误区是:很多人以为Redis完全单线程,其实从4.x开始就有后台线程了。不过用户的问题聚焦在“为什么被称为单线程”,这需要突出主流程的单线程特性。 回答结构可以这样: 1. 定义单线程的范围(主流程) 2. 解释设计原因(内存瓶颈在内存而非CPU、避免锁、IO多路复用) 3. 明多线程的例外情况(持久化、集群同步等) 4. 提版本演进(4.x异步删除、6.x IO多线程) 用户可能隐含想知道这种设计对性能的影响,但问题没直接问,暂时可以不展开。不过相关问题可以引导到性能方向。 最后生成相关问题时要扣住线程模型和性能的关系,比如版本差异、性能优化、多线程演进等。用户似乎喜欢技术细节,问题可以稍微深入些。 </think>Redis之所以被称为“单线程”,是指其**核心命令处理流程由单个主线程串行执行**,这一设计贯穿了Redis的核心服务逻辑。以下是具体解析: --- ### 一、Redis单线程”的核心体现 #### 1. **主流程的串行化** - **关键操作**:客户端请求的处理流程(读取Socket请求→解析协议→执行命令→返回结果)由单个主线程完成[^2][^3]。 - **原子性保障**:由于命令执行过程无并发竞争,无需加锁即可保证操作原子性(例如`INCR`命令的线程安全)[^2]。 - **操作示例**: ```bash SET key value # 主线程执行写操作 GET key # 主线程执行读操作 ``` #### 2. **单线程的设计原因** - **性能瓶颈在内存而非CPU**:Redis基于内存操作,执行速度极快(纳秒级),多线程切换的开销反而可能降低效率[^5]。 - **避免锁竞争**:单线程模型天然规避了多线程的锁冲突(如`LRU`淘汰策略无需锁保护数据结构)[^2]。 - **匹配I/O多路复用**:单线程配合`epoll/kqueue`等I/O多路复用技术,可高效处理数万并发连接[^5]。 --- ### 二、Redis中的“多线程”场景 尽管核心流程单线程化,但Redis仍存在多线程机制: | **版本** | **多线程用途** | **目的** | |----------|------------------------------|-----------------------------| | 4.x+ | 异步删除(`UNLINK`)、持久化`AOF` | 避免大键删除或磁盘IO阻塞主线程[^4] | | 6.x+ | 网络I/O多线程 | 分摊Socket读写压力(命令执行仍单线程)[^4] | > **示例**:在Redis 6.0中,主线程解析命令后,网络数据的读写可能由其他线程并行处理,但命令执行本身仍在主线程完成。 --- ### 三、单线程设计的优势与局限 #### 优势 ✅ - **低延迟**:单线程避免了上下文切换,适合高频轻量操作(如计数器、缓存)。 - **简化开发**:无需处理并发冲突,代码更健壮。 - **资源高效**:单核CPU即可支撑高吞吐(如1核处理10万QPS)。 #### 局限 ⚠️ - **CPU密集型操作阻塞**:`KEYS *`、复杂`Lua`脚本可能卡住整个服务。 - **多核利用不足**:无法利用多核并行执行命令(需依赖分片集群)。 --- > **总结**: > Redis的“单线程”特指**核心命令执行流程的串行化**,这一设计通过规避锁竞争、匹配I/O模型优化了吞吐量。但后台任务(持久化、删除)和网络I/O(6.x+)已引入多线程,形成“主线程单线程 + 辅助多线程”的混合架构[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值