【温故而知新】【中间件】Redis为什么这么快?

news2024/9/22 1:16:12

时间:2023年07月27日

作者:小蒋聊技术

邮箱:wei_wei10@163.com

微信:wei_wei10

【温故而知新】【中间件】Redis为什么这么快?_小蒋聊技术_免费在线阅读收听下载 - 喜马拉雅欢迎收听小蒋聊技术的类最新章节声音“【温故而知新】【中间件】Redis为什么这么快?”。大家好,欢迎来到小蒋聊技术。小蒋准备和大家一起聊聊技术的那些事。文字版材料在CSDN博客,“小蒋聊技术”的同名文章里。文字版地...icon-default.png?t=N6B9https://www.ximalaya.com/sound/652066777

前言

        大家好,欢迎来到小蒋聊技术,小蒋准备和大家一起聊聊技术的那些事。

        上次咱们一起分析了Java中的线程实现方式,这次咱们来换换,聊一下中间件Redis。

        咱们来看这样一个经典面的问题:“Redis为什么这么快?

分析

       Redis 作为优秀的内存数据库,其拥有非常高的性能。在互联网公司经常被用作缓存、分布式数据共享、分布式锁、消息队列,等等功能。官方称单个实例的OPS(Operations Per Second)能够达到10W左右。

       这里插一句,官方说的是Redis的OPS,Operations Per Second 可不是QPS。OPS是命令执行速度,这个在Redis实例中可以直接通过命令获得。但是QPS在Redis统计数据里是没有的,一般是由外部测试所得。你一次pipeline几十条命令,OPS按命令条数计数,但是QPS这一次请求可就算一次啊,差别巨大!!!

      

       咱们重新回到这个问题,Redis为什么快?这个问题,已经有很多人都发表了自己的意见和看法。所以小蒋这次分享的是一个“思考问题的思路”,答案其实并不是重点。

        咱们来尝试从语言设计和架构的角度来思考,如果你是Redis的“架构师”你会如何回答这个问题呢?

       如果你让我回答这个问题Redis为什么快?,我的答案是:“Redis就是快,它的设计思想就是‘快’, ‘快’才是Redis的目标,所以一切在新功能(Feature)的设计和研发过程中,如何‘快’是必须要考虑的问题!”

       OK,这是小蒋我个人的理解。

        Redis基于C代码实现,其核心思想就是“简单”、“高效”。所以Redis其内部采用了很多高效的机制来进行实现,以保证速度够“快”。很多公司将Redis用作“缓存”,其目的就是提供更快的响应和灵活的数据处理,这正是Redis其价值所在。

        那咱们来具体看看Redis的设计思想。

      

       (根据官网的性能测试,当data size处于合适区间时,Redis的吞吐量可达10W/s)

       (Redis benchmark | Redis) 

Redis设计思想

       首先呢,Redis将数据存储在内存中,这样读写数据的时候都不会受到磁盘I/O速度的限制,所以速度极快。也正因为Redis将数据存储在内存中,所有没有磁盘寻道(Disk Seek),另外也不会有访问地址不存在错误(Page Fault),这样将大大提升效率。

       咱们说优点明显,缺点一定突出,优缺点是相互的。内存存储虽然速度快,但是缺点就是当服务器宕机或者意外断电等场景发生时,它的数据会全部丢失,这将是一场灾难!所以,在使用Redis的时候,一定,一定要考虑在意外情况下,数据丢失这个场景和相应的处理解决方案!

       接下来,咱们再来说说Redis为什么这么快的另外一个原因,那就是它采用单线程,避免了不必要的上下文切换,同时也不存在多进程或者多线导致的用户态和内核态的频繁切换。单线程最大的优点是没有并发的安全问题,同样也不需要加锁,所以他的执行效率高。但,同样的缺点是无法利用多核CPU,线程一旦错误会引起整个应用的错误,健壮性值得考验。

      

        不过,CPU问题,官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用“单线程”的方案了

        这里小蒋多嘴插一句,这里我们一直在强调的“单线程”,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定是不止一个线程的,这里需要大家明确的注意一下!

       咱们再来分析一下Redis的数据结构,Redis其中常见的数据结构类型有:String、List、Set、Hash、ZSet这5种。Redis中的数据结构是专门进行设计的,为的就是快。它的特点是数据结构简单,对数据操作也简单。

       先来说String,Redis是C语言开发的,但在C语言中并没有​​字符串​​类型,只能使用​​ 指针​​ 和​​ 字符数组​​ 的形式来保存一个字符串。所以Redis设计了一个简单的动态字符串​​(​​​​SDS​​​​ [​​​​Simple Dynamic String​​​​])​​来作为底层实现,时间复杂度:O(1)。

      

      

         再来说List,列表 List 更多是被当作队列或栈来使用的,队列和栈的特性一个先进先出,一个先进后出。双端链表很好的支持了这些特性。

        链表里每个节点(Node)都带有两个指针,prev 指向前节点,next 指向后节点,这样在时间复杂度为 O(1) 内就能快速获取到前后节点(Node)。

        链表本身还有head 和 tail 两个参数,分别指向头节点和尾节点,这样的设计能够对双端节点的处理时间复杂度降至 O(1) ,对于队列和栈来说再适合不过。同时链表迭代时从两端都可以进行。

        另外Redis对链表长度也进行了设计,头节点里同时还有一个参数 len,和上边提到的 SDS 里类似,这里是用来记录链表长度的。因此获取链表长度时不用再遍历整个链表,直接拿到 len 值就可以了,这个时间复杂度是 O(1)。

        从以上这些,咱们可以看到Redis对于底层的数据模型都做了相应的设计,目的就是为了“快”。不过,千万别高兴的太早,一定要记得优点明显,缺点肯定就突出。看到Redis使用了这么多自定义的结构来优化“速度”,那他究竟牺牲了什么呢?

        牺牲的是“空间”和“复杂度”,因为一旦使用了额外的数据结构提升速度,那么这个数据结构里一定保存了额外的数据内容和额外的实现算法,这就是它付出的代价。

        咱们再来看一下Redis的另外一个快的原因,Redis里大量使用Hash结构,让Redis它接收到一个键值对操作后,能以微秒级速度找到数据,并快速完成操作。

        在Redis中咱们知道它是K-V这种数据类型 ,不过List、Hash、Set和Sorted Set这四种数据类型底层都有两种实现,但是它是集合。 咱们可以理解为就是一个KEY对应一个V,但是这个V是“集合”,。

        为了实现从K到V快速访问,Redis使用哈希表保存所有KV对。使用哈希表的特点是它的查找时间复杂度是O(1),无论是 10万个K还是100万K,只需一次计算,就能找到对应K的V,这样Redis的速度是不是就非常快。

        但是,别高兴太早,咱们刚刚说的是“查询”,除了查询之外还有一个“写入”呢。在实际Redis的生产环境下,因为哈希表的容量是有限的,所以随着Redis不断地向哈希表写入更多数据,这个时候就会发生哈希冲突。Redis的解决方案是通过链式哈希方式解决,也就是数组+链表的方式。

        举个例子,在哈希表有限容量的情况下,插入条目1(entry1)、条目2(entry2)、条目3(entry3),但是这三个条目哈希后的结果相同,都是3,那么这三个条目都会放在哈希表中3的位置,也有人称呼为哈希桶3,条目1(entry1)会通过一个*next指向条目2(entry2),条目2(entry2)会通过*next指向条目3(entry3),以此类推。这就形成了一个链表,也叫哈希冲突链。这个哈希冲突链越长,元素查找耗就越慢,效率就会越低。

        这个,对于目标是“快”的Redis简直是无法容忍的,所以Redis会在必要的时候对哈希表进行扩容,并且对数据进行rehash,让逐渐增多的条目(entry)元素能在更多的桶之间分散保存,减少单个桶中冲突的元素的数量,从而增加查找效率。

        理想是丰满的,现实却是骨感的。数据rehash过程中需要把原来哈表中的元素,从旧的位置映射到新的位置,这样就会有大量的数据拷贝。如果一次性全部迁移完成,就会造成Redis线程阻塞,这样Redis就无法服务其他请求了,这种情况和Redis的设计目标“快”严重不符!!!

        Redis为了解决这个问题,采用了渐进式rehash的方案,默认使用了2个全局哈希表。

  1. Redis会給哈希表2分配一个更大的空间,例如扩容到当前哈希表1的2倍大小。
  2. 紧接着Redis需要把哈当前希表1中的数据重新映射并拷贝到哈希表2种,然后释放哈希表1的空间。
  3. 但是,Redis为了实现“快”这个设计目标,采用了渐进式rehash。
  4. Redis会每处理一个请求时,从哈希表1的第一个索引位置开始,将该索引位置的中的全部数据一个接着一个的重新映射并复制到哈希表2中。
  5. Redis在处理下一个请求时,再处理哈希表1的下一个索引位置。
  6. 以此类推,Redis就把一次大的数据拷贝分摊到无数次的请求处理过程中。
  7. 从而通过空间换时间,一次换多次的方式,让Redis实现“快”这个设计目标。

        小蒋简单的翻译一下,Redis其实就是牺牲空间换取速度,直接采用了2个全局哈希表。再牺牲简单的一次性数据拷贝方案,把数据拷贝分散到无数次的请求处理中化整为零,但是这无疑牺牲了数据拷贝的处理效率,让数据拷贝的总时间变得更长,同时增加了系统设计复杂度和实现难度,在集群场景中渐进式rehash也会导致数据倾斜的问题。

       优点明显,缺点肯定就突出。

总结

        之所以Redis这么快,是因为:

  1. Redis基于存内存操作,没有耗时的磁盘IO,所以大大提升了效率;
  2. Redis是单线程工作,单线程它的优点是工作不存在操作系统的上下文切换(context switch)和锁(lock)操作。
  3. Redis的每种底层数据模型的复杂度都很低一般为O(1)及O(N),且采用了空间换时间的方式,集合类型的object都有一个字段size,读取集合长度时的复杂度为O(1),不需要遍历集合。一切向高性能靠近;
  4. Redis大量使用哈希;

        言而总之,因为Redis的产品定位,决定了Redis它的处理速度必须非常“快”,“快”就是它的产品价值所在。也正是因为这个“快的”特点,所以Redis并不适合海量数据的高性能读写,而更适合有限容量情况下的高性能操作和运算上。所以,缓存、分布式数据共享、分布式锁、计数器、等等,这些是Redis常见的使用场景。

以上是小蒋聊技术的全部分享,谢谢大家!

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

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

相关文章

【渗透测试】PNG图片隐藏部分恢复

1、图片原尺寸还原方法一 缺点就是有点慢,毕竟遍历的次数比较多 import binascii import struct import sysfilename sys.argv[1] crcbp open(filename, "rb").read() # 打开图片 crc32frombp int(crcbp[29:33].hex(), 16) # 读取图片中的CRC校验值 …

【密码学】三、AES

AES 1、AES产生2、数学基础2.1有限域GF(2^8^)2.1.1加法运算2.1.2乘法运算2.1.3x乘运算2.1.4系数在GF(2^8^)上的多项式 3、AES算法描述3.1字节代换3.2行移位3.3列混合3.4轮密钥加3.5密钥扩展 1、AES产生 征集AES算法的活动,目的是确定一个非保密的、公开的、全球免费…

Cesium态势标绘专题-直角箭头(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

国产化 | 记一次基于达梦创建数据库模式思考过程

开篇 首先,我们先来了解一下达梦数据库中用户与模式的概念,以及用户与模式之间的关系。 用户:主要是用来登录连接数据库,以及操作数据库对象等等。 模式:数据库中相关对象的集合。 关系:用户&#xff0…

测评7大热门订房APP,用好结尾这三点,分分钟帮你省掉好多钱

出去旅行预订酒店的时候,相信大家都有过纠结,那么多订房渠道到底应该选哪家。难道要把每个APP都下载下来试一遍吗? 所以,今天笔者给大家带来各大订房APP的测评。 先说结论:仅从性价比来看,民宿优于酒店&a…

Cesium态势标绘专题-燕尾箭头(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

LeetCode | Bit Manipulation, heap | 190. 191. 136. 137. 201.215.

190. Reverse Bits 191. Number of 1 Bits 一个一个数就行了。比较简单。 136. Single Number XOR的点在于,两个一样的数字a^a,结果是0. 且XOR是可以换位置的,所以把所有东西XOR在一起,剩下的就是单呗的。 137. Single Number I…

Python GDAL为具有多个波段的大量栅格图像绘制像素随时间变化走势图

本文介绍基于Python中的gdal模块,对大量长时间序列的栅格遥感影像文件,绘制其每一个波段中、若干随机指定的像元的时间序列曲线图的方法。 在之前的文章Python中GDAL批量绘制多时相栅格遥感影像的像元时间序列曲线图(https://blog.csdn.net/z…

wireshark导出H264裸流

导出H264裸流 安装wireshark下载rtp_h264_extractor.lua脚本配置lua脚本重启wireshark筛选 安装wireshark 下载抓包工具:首先,您需要下载并安装一个网络抓包工具,例如Wireshark(https://www.wireshark.org)或tcpdump&…

开源数据库 | 记一次在麒麟操作系统上适配openGauss进阶之旅

引入 适配 | 认证-Kylin V10 ARM 麒麟操作系统openGauss数据库 开篇 1、数据库架构 百度百科:openGauss 是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss采用木兰宽松许可证v2发行,提供面向多核架构的极致性能、全…

linux安装nginx遇到的报错

1、Linux如何修改只读文件(以设置自动连网为例) vim /etc/sysconfig/network-scripts/ifcfg-ens33 然后提示 E45:已设定选项“readonly”(请加!强制执行) 如果需要强制修改,可以使用&#xff0…

关于idea如何成功运行web项目

导入项目 如图 依次选择 file - new - Project from Existing Sources 选择存放的项目目录地址 如图 导入完成 点击ok 如图 依次选择 Create project from existing sources 点击next如图 ,此处默认即可 点击 next如图 点击next有该提示 是因为之前导入过…

抖音seo源码开发源代码开发技术分享

一、 抖音SEO源码开发,需要掌握以下技术: 抖音API接口:抖音提供了丰富的API接口,包括用户信息、视频信息、评论信息等。 数据爬取技术:通过抓包分析抖音接口的数据结构,可以使用Python等编程语言编写爬虫程…

Elasticsearch Query DSL

Elasticsearch Query DSL 这里使用的 Elasticsearch 的版本为 7.12.1。 1、基本概念 1.1 文档(Document) ElasticSearch 是面向文档的,文档是所有可搜索数据的最小单位,例如 MySQL 的一条数据记录。 文档会被序列化成为 json 格式,保存在…

B076-项目实战--宠物上下架 展示 领养 收购订单

目录 上下架功能提供后台宠物列表实现 前台展示前台宠物列表和详情展示店铺展示 领养分析前台后端PetControllerPetServiceImpl 订单需求分析可能产生订单的模块订单模块额外功能 订单设计表设计流程设计 集成基础代码收购订单创建订单前端后端 上下架功能提供 后台宠物列表实…

生成虚拟淘宝购买记录截图图片制作

大家都知道,淘宝购买记录截图在某些情况下非常重要,但手动制作却非常繁琐,耗费时间和精力。如果你也遇到了这个问题,那么不妨试试淘宝订单生成器,它能够帮助你轻松生成淘宝购买记录截图,提升工作效率。 虚拟…

Docker 容器高级操作

Docker容器高级操作 Docker容器创建、停止、启动、删除等基础操作上篇已述,然Docker容器被广大开发者青睐,不可能只有如此简单的功能,必有高阶功法。那么接下来 让我们一同走进容器操作的高级篇,领略其高级操作的魅力。 查看容器 docker ps -a | grep tomcat [root@tudou…

【数据结构】实验十:哈夫曼编码

实验十 哈夫曼编码 一、实验目的与要求 1)掌握树、森林与二叉树的转换; 2)掌握哈夫曼树和哈夫曼编码算法的实现; 二、 实验内容 1. 请编程实现如图所示的树转化为二叉树。 2. 编程实现一个哈夫曼编码系统,系统功能…

PingCAP 陈煜琦:深耕中国市场,构建客户成功生态

在 PingCAP 用户峰会 2023 上,PingCAP 副总裁陈煜琦分享了“激流入海,PingCAP 中国业务发展策略”的演讲,介绍了 PingCAP 在技术层面的发展方向,强调了 PingCAP 服务于中国企业客户的重要性,并介绍了 PingCAP 助力 客户…

Pytest学习教程_基础知识(一)

前言 pytest是一个用于编写和执行Python单元测试的框架。它提供了丰富的功能和灵活性,使得编写和运行测试变得简单而高效。 pytest的一些主要特点和解释如下: 自动发现测试:pytest会自动查找以"test_"开头的文件、类和函数&#x…