为什么单线程的Redis如此的快(Why is single-threaded Redis so fast)

news2025/7/15 1:25:10

视频地址:YouTube

Why is Redis so fast? What fundamental design decisions did the developers make more than a decade ago that stood to test of time. Let’s take a look. Redis is a very popular in-memory database. It’s rock solid, easy to use, and fast. These attributes explain why it is one of the most loved databases according to the Stack Overflow’s annual developer survey.
The first reason Redis is fast is because it is an in-memory database. Memory access is several orders of magnitude faster than random disk I/O. Pure memory access provides high read and write throughput and low latency. The trade-off is that the dataset cannot be larger than memory.
Code-wise, in-memory data structures are also much easier to implement than the on-disk counterparts. This keeps the code simple, and it contributes to Redis’ rock solid stability.
Another reason Redis is fast is a bit unintuitive. It is primarily single threaded. Why would a single threaded design lead to high performance? Wouldn’t it be faster if it uses threads to leverage all the CPU cores? Multi-threaded applications require locks or other synchronization mechanisms. They are notoriously hard to reason about. In many applications, the added complexity is bug prone and sacrifices stability, making it difficult to justify the performance gain. In the case of Redis, the single threaded code path is easy to understand. How does a single threaded codebase handle many thousands of incoming requests and outgoing responses at the same time? Won’t the thread get blocked waiting for the completion of each request individually? Now, this is where I/O multiplexing comes into the picture. With I/O multiplexing, the operating system allows a single thread to wait on many socket connections simultaneously. Traditionally, this is done with the select or poll system calls. These system calls are not very performant when there are many thousands of connections. On linux, epoll is a performant variant of I/O multiplexing that supports many many thousands of connections in constant time. A drawback of this single threaded design is that it does not leverage all the CPU cores available in modern hardware. For some workloads, it is not uncommon to have several Redis instances running on a single server to utilize more CPU cores.
We alluded to the third reason why Redis is fast. Since Redis is an in-memory database, it could leverage several efficient low-level data structures without worrying about how to persist them to disk efficiently - linked list, skip list and hash table are some examples.
It is true that there are attempts at implementing new Redis compatible servers to squeeze more performance out of a single server. With Redis ease of use, rock solid stability, and performance, it is in our view that Redis still provides the best performance and stability tradeoff in the market. If you’d like to learn more about system design, check out our books and weekly newsletter. Please subscribe if you learned something new. Thank you so much, and we’ll see you next time.

专业词汇:
1)single-threaded:单线程的
2)in-memory database:内存数据库
3)memory access:内存访问
4)several orders of magnitude:几个数量级(大小的目录)(这里是adv)
5)high read and write throughput and low latency:高读写吞吐量和低延迟
6)code-wise:从代码的角度(wise作为后缀,翻译成从什么角度)
7)synchronization mechanisms:同步机制
8)sacrifices stability:牺牲稳定性
9)bug-prone:有bug倾向的
10)performance gain:性能增益
11)code path:代码路径
12)codebase:代码库
13)incoming requests and outgoing responses:传入的请求和传出的响应
14)I/O multiplexing:I/O多路复用
15)performant:性能好的
16)system calls:系统调用
17)in constant time:线性时间(O(n),也就是东西越多、越废时间)
18)workloads:工作负荷
19)linked list:链表
20)skip list:跳表
21)hash table:哈希表
22)squeeze more performance:榨取更多性能
23)out of …:从(数个)里、由于(原因,动机)、用…(材料)

好次积累:
1)stand the test of time:经受住时间的考验
2)rock soild:稳定的、可靠的
3)the most loved:最受欢迎的
4)trade-off:权衡
5)counterparts:类似that,替换前面的data structure
6)unintuitive:不直观的
7)leverage:利用
8)reason about:推断
9)notoriously hard:臭名昭著的困难
10)many thousands of:数以千计(many加不加都一样)
11)variant:变体
12)allude to:简介提到
13)weekly newsletter:每周的简讯
14)loop:循环

文章批注:
1)“Memory access is several orders of magnitude faster than random disk I/O.”
这里说的是随机I/O,也有顺序I/O,英文叫Sequential I/O。当读取第一个block时,要经历寻道,旋转延迟,传输三个步骤才能读取完这个block的数据。而对于下一个block,如果它在磁盘的某个位置,访问它会同样经历寻道,旋转,延时,传输才能读取完这个block的数据,我们把这种方式的IO叫做随机IO,但是如果这个block的起始扇区刚好在我刚才访问的block的后面,磁头就能立刻遇到,不需等待,直接传输,这种IO就叫顺序IO。
其中顺序I/O全面碾压随机I/O,是因为机械硬盘采用传统的磁头探针结构,随机读写时需要频繁寻道,也就需要磁头和探针频繁的转动,而机械结构的磁头和探针的位置调整是十分费时的,这就严重影响到硬盘的寻址速度,进而影响到随机写入速度。

2)“Pure memory access provides high read and write throughput and low latency.”
吞吐量是指系统在单位时间内处理请求的数量。因为纯内存访问响应的快,所以吞吐量大、延迟小。
为什么内存就快呢?内存直接由CPU控制,也就是CPU内部集成的内存控制器,所以说内存是直接与CPU对接,享受与CPU通信的最优带宽,然而硬盘则是通过主板上的桥接芯片(因为硬盘数据量太大,直接编址数据线成本太高,所以加了中间物)与CPU相连,所以说速度比较慢。

3)“With I/O multiplexing, the operating system allows a single thread to wait on many socket connections simultaneously.”
对比多线程,每有一个新的请求,就会创一个线程去解决,也就是并发的去解决请求,这样带来的问题,就像文中所说,线程之间的同步和加锁控制比较麻烦,增加代码的复杂度,而且一个进程出了问题可能会影响别的线程(因为线程不是进程,它们是共用内存的)。还有销毁线程时需要消耗内存资源以及占用CPU时间,也会带来线程切换的性能开销。
而I/O多路复用,就是说,多个IO的“路”复用一个“进程”。可以理解为,用单线程实现出多线程的效果。它的“多线程”,也只是针对处理网络请求过程采用了“多线程”,而数据的读写命令,仍然是单线程处理的。

具体说一下I/O多路复用,首先你要知道,所有的系统IO都分为两个阶段:等待就绪和操作。例如:读函数分为等待系统可读和真正的读;同理,写函数分为等待可写和真正的写。
传统的方式是当你调用read时,如果没有数据收到,也就是等待阶段,那么线程就会被挂起,直到收到数据(阻塞I/O)或者直接中断这个请求(非阻塞I/O);而使用I/O多路复用,比如有多个客户端发起了read,如果没有数据收到,需要等待,服务端就会对他们监听,然后一个一个进行轮巡,哪个好了,可以进入执行阶段,就让线程开始执行,然后最终返回给客户端。这样做的好处就是用更少的内存等资源来支持更高的并发数,节省资源。
而且因为采用单线程,避免了不必要的上下文切换(虚拟内存、栈、全局变量等用户空间的资源,单线程也会有,但没有多线程多、复杂)和竞争条件,不用考虑加锁释放锁和死锁的问题。
在这里插入图片描述
线程池(多线程)和I/O多路复用所以它俩到底谁更好?可以看看下面这篇文章,知乎文章:IO多路复用和线程池哪个效率更高,更有优势?。线程池其实就是升级版的多线程,线程池是在程序运行开始,创建好的n个线程,并且这n个线程挂起等待任务的到来。而多线程是在任务到来得时候进行创建,然后执行任务;线程池中的线程执行完之后不会回收线程,会继续将线程放在等待队列中,多线程程序在每次任务完成之后会回收该线程;由于线程池中线程是创建好的,所以在效率上相对于多线程会高很多;线程池也在高并发的情况下有着较好的性能不容易挂掉,多线程在创建线程数较多的情况下,很容易挂掉。
这篇文章也不错:解释了上面知乎呢篇文章,总之就是个有千秋,而且也能结合使用,让多线程中的每个线程都能管理一部分多路复用,既发挥了多核优势,又避免大量的线程切换。

下面这段话可能会让你有更深的理解:

线程池的诞生, 主要是为了减少进程频繁创建和回收带来的性能损失。但它本质上, 依然是一个CPU执行单元在请求的生命周期内, 只能服务这一个请求,是被一个请求独占的。线程创建是需要成本的,一般一个线程要耗掉1m左右的内存,当线程数达到一个界限时,cpu频繁调度线程进行上下文切换所带来的性能损耗, 将会非常可观,甚至可以让你的服务不可用。
因此,要解决这个问题,有两个路子可走,一,能否做到一个线程同时(交叉)处理多个请求,不被—个请求独占。二,换比线程更细小的执行单元。
而lO多路复用,就是要让一个CPU执行单元同时服务n个请求。这与多进程、多线程、线程池以同步IO的形式处理请求有本质上的区别了。原理上,它是让内核进程去批量管理多个请求的IO事件,在lO事件就绪后通知用户进程/线程处理IO事件,或者提供接口(系统调用,如select)给用户进程/线程去查询就绪的lO事件,避免用户进程/线程在某一个请求上干等,这样一个用户进程/线程就可以交叉处理多个请求了,而不是被一个请求独占。
但lO多路复用不是独立的,往往是跟线程池结合使用的。像redis那样单线程处理请求的软件真的是少数,最近听说redis也开发多线程版本了,每一个线程依然是多路复用的。这几年有个比较火的概念,就是协程,这是语言层面实现的更轻粒度的执行单元,比线程更节省资源,跟用线程替代进程处理服务的一个性质,就是换更小的执行单元。
总结一下:
如果并发量不是特别大,机器资源充足,基于线程池的server处理高并发就足够了,开发简单,容易维护。
如果并发量非常大,公司比较重视机器成本,那可以考虑开发基于IO多路复用的server。缺点就是开发难度高,维护成本高。

文章末尾说IO多路复用难度大,应该指的是多线程+IO多路复用结合的方式。

4)“Traditionally, this is done with the select or poll system calls. These system calls are not very performant when there are many thousands of connections. On linux, epoll is a performant variant of I/O multiplexing that supports many many thousands of connections in constant time.”
比较高级,简单说明:

  • select:服务端一直在轮询、监听如果有客户端链接上来就创建一个连接放到数组A中,继续轮询这个数组,如果在轮询的过程中有客户端发生IO事件就去处理;select只能监视1024个连接(一个进程只能创建1024个文件);而且存在线程安全问题。
  • poll:在select做了许多修复,比如不限制监测的连接数;但是也有线程安全问题。
  • epoll:也是监测IO事件,但是如果发生Io事件,它会告诉你是哪个连接发生了事件,就不用再轮询访问。而且它是线程安全的,但是只有linux平台支持。

总结

在这里插入图片描述

  1. Redis is a RAM-based database. RAM access is at least 1000 times faster than random disk access.

  2. Redis leverages IO multiplexing and single-threaded execution loop for execution efficiency.

  3. Redis leverages several efficient lower-level data structures.

简单来讲,快的三个原因:
1)Redis是基于内存存储的数据库,比传统的硬盘存储访问速度更快。这是因为基于内存的访问,直接受CPU调控,比起需要桥接芯片的硬盘访问,会有更大的吞吐量和更低的延迟。

2)Redis利用I/O多路复用,实现类多线程的效果。一方面Redis的操作基本都是基于内存的,CPU资源根本就不是Redis的性能瓶颈,没必要多线程。
多线程会带来锁、同步机制等,让代码变得更复杂,使得发生bug的可能性增大、降低稳定性。
使用单线程模型,可维护性更高,开发,调试和维护的成本更低。并且上下文切换带来的开销更小,更节约资源。
并且利用linux中的epoll,在单线程同时去等待许多个scoket请求,可以实现更高的性能。

在多路复用的IO模型中,在处理网络请求时,调用 select (其他函数同理)的过程是阻塞的(看上面那张图)(因为是单线程),也就是说这个过程会阻塞线程,如果并发量很高,此处可能会成为瓶颈。
如果能采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。
所以Redis 6.0中也是使用了多线程,但是只有在网络请求的接收和解析,以及请求后的数据通过网络返回给时,使用了多线程。而数据读写操作还是由单线程来完成的。
这篇讲Redis为什么不用多线程也可以看看。

3)Redis由于利用了内存存储和I/O多路复用,使得代码逻辑更加简单,可以利用简单且高效的数据结构(因为多线程的话,你得使用更复杂的、线程安全的那种数据结构,肯定会降低性能),并且进行了重新设计(看那张图,比如List,是由LinkedList和ZipList结合实现的),从而使得性能快速而稳定。

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

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

相关文章

HittER: Hierarchical Transformers for Knowledge Graph Embeddings

HittER: Hierarchical Transformers for Knowledge Graph Embeddings HittER由两部分组成: 1,底部:Entity Transformer 源实体的局部邻居的每个实体-关系对的特征提取。负责将实体关系对中所有有用特征打包成向量,以供顶部块使用…

mysql基础部分第一次复习(1-8章,到聚合函数)

基本的使用 show databases; create database 数据库名; //创建数据库 use 数据库名; //使用数据库 使用完use语句之后接下来的SQL操作都是针对当前数据库的了show tables; //查看某个数据库的所有表格 show tables from 数据库名;create table 表名称{…

C++--智能指针--1123

1.智能指针解决的问题 int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");return a / b; } void Func() { // 1、如果p1这里new 抛异常会导致p1不会Delete而导致内存泄漏 // 2、如果p2这里new 抛异常会导致p1和p2都不会de…

【数据结构学习笔记】18:线段树(单点修改)

1 线段树上的操作 push_up(int u):由子节点的信息去计算父节点的信息,例如两个子节点的区间和,加起来就是父节点表示的区间和。其中u是当前节点编号,表示用u的左右两个子节点来算一下自己这个节点的信息。push_down:将…

流氓设备检测和预防

自带设备 (BYOD) 策略中涉及的设备以及这些设备连接到的端口具有多个通信路径。确保这些设备及其路径在进入组织网络时立即被检测、评估和管理至关重要,因为非托管设备很容易成为安全风险。但是,在整个企业网络中同时添加许多设备…

反射、枚举、lambda——小记

文章目录反射反射定义反射相关的类Class 类反射示例获得Class对象的三种方式反射使用 ——代码面试题:你知道有几种创建对象的方式吗?反射优点和缺点枚举Lambda表达式概念Lambda表达式的语法代码反射 反射定义 Java的反射(reflection)机制是在运行状态…

路由策略和路由控制

路由策略和路由控制 路由策略 针对路由的发布,接收,引入进行控制,从而影响数据的路径或者可达性 路由匹配工具 ACL:访问前缀列表 一个ACL用多条规则组成,不同规则之间通过rule id进行区分,默认rule 步…

(附源码)python办公数据分析系统 毕业设计 021836

Python办公数据分析系统 摘 要 现代办公通过办公自动化系统可以大大提高的效率、节省成本、规范业务和流程,辅助提升管理水平。办公系统在单位信息化中占有非常重要的地位,涉及到单位的各个部门及绝大多数人员,流程和协作方面要求非常强。 办…

[附源码]java毕业设计英语知识竞赛报名系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

NDK 是什么 | FFmpeg 5.0 编译 so 库

前言 NDK 全称 Native Development Kit,也就是原生开发工具包 ,官网对它有详细的 中文介绍 。可能一说到 NDK 或 JNI ,大家脑子里第一反应就是集成 C/C 。其实 JNI 的含义是 Java Native Interface ,这种接口允许 Java 和其他语言…

SpringBoot SpringBoot 原理篇 1 自动配置 1.3 bean 的加载方式【三】

SpringBoot 【黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇1 自动配置1.3 bean 的加载方式【三】1.3.1 第三种方式1 自动配置 1.3 bean …

体系结构26_输入输出系统(3)

盘阵列(RAID) 盘阵列容量大、速度快、可靠性高、造价低廉。它是目前解决计算机I/O瓶颈的有效方法之一,有着广阔的发展前景。 盘阵列有多种组织方式: RAID 0 亦称数据分块(Striping),即把数据分…

推特群推掀开营销新篇章

与Facebook和Instagram相比,Twitter营销并不是一个非常热门的营销渠道,对于跨境卖家来说可能会有一些陌生和挑战,但是作为一个重要的营销渠道,Twitter在全球市场上拥有超过1.45亿的日活跃用户(超过3.26亿的月活跃用户)&#xff0c…

Pinia基本使用

文章目录1. 介绍2. Pinia 和 Vuex3. 安装和基本使用4. pinia修改数据状态5. pinia持久化处理6. 自定义插件1. 介绍 它是2019 年 11 月对于新版本的vue提供的组合Api进行的尝试,它可以很好的集合vue新的api方法,且还很好的支持ts的写法,Pinia…

web前端-javascript-运算符的优先级(如果遇到的优先级不清楚的,可以使用()来改变优先级)

文章目录运算符的优先级1. , 运算符2. 优先级2.1. 就和数学中一样,在 JS 中运算符也有优先级2.2. 在 JS 中有一个运算符优先级的表2.3. 但是这个表我们并不需要记忆2.3. &&和||的优先级运算符的优先级 var a, b, c;//var a1, b2 , c3; //alert(b);//var re…

sql server如何卸载干净?来看这里

一、如何卸载干净 1.关闭服务 快捷键:windows R,在命令行输入: services.msc,把有关SQL都关闭 ,下图所示: 2.到控制面板,卸载 sql server 3.删除磁盘里的文件 我的在c盘里,看各位…

你了解专利的快速预审嘛?

随着经济的发展和科技创新步伐的加快,我国专利申请量的增长速度已大大高于专利审结的速度。专利审查周期的长短不仅影响企业对市场的可预期性,而且影响专利系统对技术创新的产出和扩散的激励作用的发挥。过长的专利审查周期可能会影响企业的竞争预期和获…

[附源码]java毕业设计影院售票系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

为什么选择WordPress作为企业CMS?

WordPress 是世界上最受欢迎的内容管理系统 (CMS)。它为超过40% 的网站和超过 64% 的使用 CMS 的网站提供支持。它易于使用和定制。但它是企业网站的最佳选择吗? 随着大公司意识到它能够构建一个可以根据他们的需求扩展的强大网站的能力,WordPress持续流…

JVM知识体系学习一:JVM基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用

文章目录前言一、JVM基础1、cross platform 跨平台2、cross language 跨语言3、什么是JVM呢?一张图告诉你4、java从编码到执行*****5. 从跨平台的语言到跨语言的平台6. jvm与class文件格式7. JVM8. javac的过程9. 常见的JVM实现10. JDK JRE JVM二、Class File Forma…