redis 为什么这么快,除了io多路复用还有什么?
off999 2025-03-30 20:03 31 浏览 0 评论
前言
相信大家在面试过程中,都被面试官问到过这样一个问题,缓存中间件大名鼎鼎的 redis 速度为什么这么快呢?
针对于面试过程中的痛点问题,笔者昨晚熬夜收集资料,并且通过走访大量使用者,整理出如下的结论。我敢保证,你看了这篇文章,再问你这个问题,保准把面试官虐哭。
分析原因
这里就不卖关子了,先说结论,我们再对原因进行抽丝剥茧。
redis快的原因
- 纯内存操作(最主要条件)
- 合适的线程模型
- 优秀的数据结构
- 合理的数据编码方式
纯内存操作(最主要条件)
首先最主要的原因一定是:redis 的数据操作是基于内存的。
众所周知,内存的访问速度是远远大于硬盘访问速度的。我们来做个对比,拿数据库(硬盘)和 redis (内存)对比,一个操作对应磁盘,一个操作对应内存。他们两个的的访问速度差了一个数量级。
可能大家对数量级没有什么该概念。那可是整整 1000 倍啊!现在大家知道了吧。
下面是我精心为大家准备的访问速度图,已经验证,方便大家食用。
有的小伙伴要问了,redis不是有数据持久化吗?
怎么会是只操作内存呢?
这位同学一看就是对 redis 理解的不够透彻,redis 持久化线程和操作内存数据的线程,并不是一个线程,我们这里说的 redis 快,只是针对操作内存的线程来说的,操作很快。因为操作是直接客户端响应时间息息相关的。
合适的线程模型
单线程误区
先说一个常见误区,就是大家在其他的文章或者面试题中,通过一段时间的以讹传讹。
大家都认为 redis 速度快,有一个原因是单线程!!!
在这里先反驳一下这个观点,这个结论是个明显的因果颠倒,主次不分的结论!!
为什么这么说呢?
首先单线程 redis 不是纯粹的单线程,我们所知晓的 redis 单线程只有 网络请求模块和 数据操作模块是单线程的。
你要知道多线程的出现,本身就是为了解决多核心 CPU 利用率不足。如果单线程是快的原因的话,那么我们还要多线程干什么,小伙伴你说是吧!
那为什么不用多线程呢?
因为没有必要,我们先来想一下多线程适用场景有哪些?
在这之前,我们先了解一下计算机在运行过程中的主要操作分为以下两种:
- CPU 计算操作
- 网络和磁盘 IO 读取操作
多线程的出现以及适用场景目的就是为了提升单线程在 cpu利用率和io利用率之间的不足
那么redis怎么不需要多线程呢?
我们从上面的计算机操作分类来说
- 从 CPU 计算来说,redis 操作都是基于内存操作,很少有一些耗费CPU 的计算操作,对于 redis 操作来说,CPU 计算不是瓶颈,瓶颈在于 redis 操作内存的速度。
- 从网络 IO 来说,redis 的 网络 IO 接收事件,确实是一个很需要去提升的点 因为所有客户端对 redis 的操作 最终都会由 redis 的网络模块接受 ,所以对于 redis 来说 提升网络 IO 的利用率 很有必要。
结论:redis 在 CPU 计算上不存在瓶颈,性能瓶颈只存在于网络 IO 中
但是~想要提高网络 IO 的利用率,不是只有多线程一条路。
- redis 由于历史遗留原因最终没有采用多线程处理网络 IO 而是采取了 单线程 + 多路复用器处理。
- 在2020年,redis 6.0版本 已经开始做多线程处理网络 IO ,性能提升巨大。
所以说千万不要陷入 redis 是单线程所以快,现在是 CPU 多核心时代,最差的情况 CPU 一个核心绑定一个线程,不同线程之间也不处理竞态资源,也比你只用到一个核心处理快的多了吧!
我这里总结一下我的看法,redis 6.0版本 之前采用单线程处理网络 IO 原因如下:
- redis 为了保持一贯的语义,采用了单线程模型(也许是懒吧)
- 就算是单线程,也完全足够用了,在不需要超大并发的情况下,也不需要上多线程。
那为什么redis 6.0 还是采用了多线程处理网络 IO ?
不是说多路复用技术已经大大的提升了网络 IO 利用率了么,为啥还需要多线程?
主要是因为我们对 Redis 有着更高的要求。
据测算,redis 将所有数据放在内存中,内存的响应时长大约为 100 纳秒,对于小数据包,redis 服务器可以处理 80,000 到 100,000 QPS,这么高的对于 80% 的公司来说,单线程的 redis 已经足够使用了。
但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的 QPS。
为了提升 QPS,很多公司的做法是部署 redis集群,并且尽可能提升 redis 机器数。但是这种做法的资源消耗是巨大的。
而经过分析,限制 redis 的性能的主要瓶颈出现在网络IO的处理上,虽然之前采用了多路复用技术。但是我们前面也提到过,多路复用的 IO 模型本质上仍然是同步阻塞型 IO 模型。
下图是 redis 网络 IO 多线程和单线程的速度对比图,大家都来看一下
从上面可以看到 `GET/SET` 命令在 4 线程 IO 时性能相比单线程是几乎是翻倍了。
结论:redis 6.0 多线程网络 IO 性能提升巨大,不严谨条件下几乎翻倍
单线程模式下快的原因
说完了对于单线程的误区,下面我们就要说一下在单线程模式下,redis 快的原因
用四个字就可以来形容这个原因,那就是 多路复用
多路复用是啥呢?
简单理解就是 单个线程同时检测若干个网络连接(Socket)是否可以执行IO操作的能力,就是将多个进程的网络 IO Socket注册到同一个管道上。 用最少的资源,干最多的事情。
比较传统的方式是使用多线程模型,每来一个客户端连接,就分配一个线程,然后后续的读写都在对应的进程/线程,这种方式处理 100 个客户端没问题,但是当客户端增大到 10000 个时,10000 个进程/线程的调度、上下文切换以及它们占用的内存,都会成为瓶颈。
为了解决上面这个问题,就出现了 I/O 的多路复用,可以只在一个进程里处理多个文件的 I/O
所有的 io 操作 由这一个管道来和内核进行统一交互 管道中的io请求数据准备好之后 管道会将数据 拷贝到用户空间中。
在 redis 中,每当一个套接字准备好执行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。
但是出现的个数不可能有很多,这样我们用一个线程来监听这些消息,然后分派去给操作线程执行就可以。
多路复用模型显而易见的好处:
- 单线程,省去多个线程创建和上下文切换操作
- Socket 连接不多的情况,性能甚至比多线程有可能要高
相关视频推荐
6种epoll的做法,从redis,memcached到nginx的网络模型实现
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
优秀的数据结构
redis 在存储数据结构的设计上还是花了一番心思的 不然这也不会成为它快的一点原因。
它拥有五种常用的数据类型,帮助我们在各种各样的场景上都能灵活应对,并且也能用对应的数据类型做出很多很有意思的事情。
redis的五种数据类型主要有:
- String 整数浮点数或者字符串
- Set 集合
- Zset 有序集合
- Hash 散列表
- List 列表
举个例子,我们都知道redis底层使用c语言写的,但是 String 实现并不是简单的用c语言的字符串实现的,而是采用一个 SDS(简单动态字符串) 的结构体来实现的。
Tips:这里给大家补充一个知识点,C语言的String是以 “\0” 结尾的,并且计算长度是得通过遍历获取,时间复杂度是 O(n) , 而且在数据中不能有 “\0” 出现,会导致计算错误。
redis String 的 SDS 结构体,不仅让长度的获取是 O(1) 操作,而且二进制安全,没有特殊字符的限制,可以存储视频图片的数据。
这里我想让大家知道,redis 数据结构设计考虑还是很优秀的。
数据结构的详细解析,我们在这里就不多赘述了。
合理的数据编码方式
怎么理解这个合理的编码方式呢?
我们还是以 redis 中的 string 结构举例子,redis为了存储不同大小的字符串,精心设计了 5 种类型。
sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64
它们的不同就是,本身的数据类型不同,举个例子
- sdshdr16 中的字段分配大小是 uint16 2^16
- sdshdr32 中的字段分配大小是 uint32 2^32
结论: 通过这种能够灵活存储不同大小的字符串,有效的节省了内存。字符串大的时候分配大的内存空间,小的时候就分配小的,高效利用内存。
当然,在这里只是举个例子。
总结
至此,我们知道了这个 redis 经典面试题的答案了。
原因无他。分别是:
- 纯内存操作
- 多路复用线程模型
- 优秀的数据结构
- 合理的数据编码方式
不知道大家学会了吗? 学会的小伙伴们,再也不怕那些刁钻的面试官了,我敢保证,你看了这篇文章,再问你这个问题,保准把面试官虐哭。
相关推荐
- 使用 python-fire 快速构建 CLI_如何搭建python项目架构
-
命令行应用程序是开发人员最好的朋友。想快速完成某事?只需敲击几下键盘,您就已经拥有了想要的东西。Python是许多开发人员在需要快速组合某些东西时选择的第一语言。但是我们拼凑起来的东西在大多数时候并...
- Python 闭包:从底层逻辑到实战避坑,附安全防护指南
-
一、闭包到底是什么?你可以把闭包理解成一个"带记忆的函数"。它诞生时会悄悄记下自己周围的变量,哪怕跑到别的地方执行,这些"记忆"也不会丢失。就像有人出门时总会带上...
- 使用Python实现九九乘法表的打印_用python打印一个九九乘法表
-
任务要求九九乘法表的结构如下:1×1=11×2=22×2=41×3=32×3=63×3=9...1×9=92×9=18...9×9=81使用Python编写程序,按照上述格式打印出完整的九...
- 吊打面试官(四)--Java语法基础运算符一文全掌握
-
简介本文介绍了Java运算符相关知识,包含运算规则,运算符使用经验,特殊运算符注意事项等,全文5400字。熟悉了这些内容,在运算符这块就可以吊打面试官了。Java运算符的规则与特性1.贪心规则(Ma...
- Python三目运算基础与进阶_python三目运算符判断三个变量
-
#头条创作挑战赛#Python中你学会了三步运算,你将会省去很多无用的代码,我接下来由基础到进阶的方式讲解Python三目运算基础在Python中,三目运算符也称为条件表达式。它可以通过一行代码实现条...
- Python 中 必须掌握的 20 个核心函数——set()详解
-
set()是Python中用于创建集合的核心函数,集合是一种无序、不重复元素的容器,非常适合用于成员检测、去重和数学集合运算。一、set()的基本用法1.1创建空集合#创建空集合empty_se...
- 15个让Python编码效率翻倍的实用技巧
-
在软件开发领域,代码质量往往比代码数量更重要。本文整理的15个Python编码技巧,源自开发者在真实项目中验证过的工作方法,能够帮助您用更简洁的代码实现更清晰的逻辑。这些技巧覆盖基础语法优化到高级特性...
- 《Python从小白到入门》自学课程目录汇总(和猫妹学Python)
-
小朋友们好,大朋友们好!不知不觉,这套猫妹自学Python基础课程已经结束了,猫妹体会到了水滴石穿的力量。水一直向下滴,时间长了能把石头滴穿。只要坚持不懈,细微之力也能做出很难办的事。就比如咱们的学习...
- 8÷2(2+2) 等于1还是16?国外网友为这道小学数学题吵疯了……
-
近日,国外网友因为一道小学数学题在推特上争得热火朝天。事情的起因是一个推特网友@pjmdoll发布了一条推文,让他的关注者解答一道数学题:Viralmathequationshavebeen...
- Python学不会来打我(21)python表达式知识点汇总
-
在Python中,表达式是由变量、运算符、函数调用等组合而成的语句,用于产生值或执行特定操作。以下是对Python中常见表达式的详细讲解:1.1算术表达式涉及数学运算的表达式。例如:a=5b...
- Python运算符:数学助手,轻松拿咧
-
Python中的运算符就像是生活中的数学助手,帮助我们快速准确地完成这些计算。比如购物时计算总价、做家务时分配任务等。这篇文章就来详细聊聊Python中的各种运算符,并通过实际代码示例帮助你更好地理解...
- Python学不会来打我(17)逻辑运算符的使用方法与使用场景
-
在Python编程中,逻辑运算符(LogicalOperators)是用于组合多个条件表达式的关键工具。它们可以将多个布尔表达式连接起来,形成更复杂的判断逻辑,并返回一个布尔值(True或Fa...
- Python编程基础:运算符的优先级_python中的运算符优先级问题
-
多个运算符同时出现在一个表达式中时,先执行哪个,后执行哪个,这就涉及运算符的优先级。如数学表达式,有+、-、×、÷、()等,优先级顺序是()、×、÷、+、-,如5+(5-3)×4÷2,先计算(5-3)...
- Python运算符与表达式_python中运算符&的功能
-
一、运算符分类总览1.Python运算符全景图2.运算符优先级表表1.3.1Python运算符优先级(从高到低)优先级运算符描述结合性1**指数右→左2~+-位非/一元加减右→左3*//...
- Python操作Excel:从基础到高级的深度实践
-
Python凭借其丰富的库生态系统,已成为自动化处理Excel数据的强大工具。本文将深入探讨五个关键领域,通过实际代码示例展示如何利用Python进行高效的Excel操作,涵盖数据处理、格式控制、可视...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 使用 python-fire 快速构建 CLI_如何搭建python项目架构
- Python 闭包:从底层逻辑到实战避坑,附安全防护指南
- 使用Python实现九九乘法表的打印_用python打印一个九九乘法表
- 吊打面试官(四)--Java语法基础运算符一文全掌握
- Python三目运算基础与进阶_python三目运算符判断三个变量
- Python 中 必须掌握的 20 个核心函数——set()详解
- 15个让Python编码效率翻倍的实用技巧
- 《Python从小白到入门》自学课程目录汇总(和猫妹学Python)
- 8÷2(2+2) 等于1还是16?国外网友为这道小学数学题吵疯了……
- Python学不会来打我(21)python表达式知识点汇总
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)