为什么单线程的Redis那么快?

2023-11-16

1. Redis单线程的本质

其实,Redis并不是单线程,我们之所以会一直称Redis是单线程,这是因为Redis在处理客户端的读写请求时,只有一个主线程,而在处理以下这些操作时,Redisfork出其他的子线程来处理:

  • 主从数据同步
  • 切片集群数据同步
  • 过期键值异步删除
  • AOF或RDB持久化

所以整体来看Redis并不是单线程。

Redis6.0中引入的多线程机制,实际上只是将网络IO读写处理这块逻辑变成多线程,因为在Redis6.0以前的版本中, 网络IO请求也是在主线程中进行处理,随着互联网应用的高并发访问以及网络硬件性能的提升,在主线程中进行网络IO处理已经成为Redis的瓶颈,因此使用多线程来处理网络IO请求,可以显示提高Redis响应速度,而对于键值对的读写,仍然由主线程一个线程进行处理,这样可以仍然可以不用加锁也能保证读写操作的原子性,避免多线程互斥机制带来的性能损耗。

2. 单线程为什么那么快?

通过前面的介绍,我们知道,Redis并非完全是单线程,但在处理网络IO和数据读写等Redis核心功能时,Redis确实是由主线程处理的,那么我们就不禁有个疑问,Redis的单线程为什么那么快呢?

总结了以下大概有以下四个方面的原因:

  • 内存操作
  • 高效的底层数据结构
  • 多路复用IO模型
  • 避免多线程切换开销

2.1 内存操作

Redis是内存数据库,读写操作都在内存中,学过计算机基础的我们都知道,CPU读取内存的速度要比读取磁盘的速度快得多,所以单台Redis服务器每秒能处理数十万的读取也就不足为怪了,事实上,基于内存的读写操作,是Redis能这么快的最重要的原因。

2.2 高效的底层数据结构

我们都知道Redis提供了五种非常好用的数据类型:StringListHashSetSorted Set

Redis有六种底层数据结构,分别为哈希表,压缩列表,跳表,整数数组,简单动态字符串。

数据类型与其底层数据结构对应关系如下图所示

总结来说就是:

  • String:Redis没有使用C语言内置的字符数组,而是将字符数组封装为简单动态字符串(SDS),虽然SDS比C语言原生字符数组更费内存,但通过空间换时间,可以将很多C语言字符数组时间复杂度为O(n)的操作转为O(1),提高处理速度,比如strlen命令获取String长度时,对于C语言的字符数组,需要遍历数组才能知道,时间复杂度为O(n),而简单动态字符串已经保存了字符串的长度,因此可以直接获取,时间复杂度为O(1)。

  • Hash:在数据量还不多的情况下,Hash类型使用压缩列表(ZipList)保存元素,因为元素不多,所以查找也比较快,如果数据增长到设定的值,就改为哈希表,而哈希表查找元素的时间复杂度为O(1),所以也是相当快的。

  • List:List的底层数据结构为双向链表和压缩列表组合而成的,也称为快速链表(QuickList),因此List类型非常适合头尾插入弹出的操作,因此如果要把Redis作为队列的话,选择List是非常高效的。

  • Sorted Set:当元素数量不多时,Sorted Set使用的是压缩列表,只有当元素超过设定值时,才使用跳表和哈希表。

  • Set:在元素的数值都是整数时,Set使用整数数组保存数据,如果元素数量达到设置的值,则改为哈希表。

Redis是一个Key-Value健值对数据库,我们上面介绍的五种类型是指Key-Value中的Value,对于所有KeyValue之间的映射,Redis也是采用哈希表,这个哈希表也叫全局哈希表,如下图所示:

2.3 多路复用IO模型

Redis高性能的另一个重要的原因是采用多路复用IO模型,由于Redis是单线程处理网络请求的,如果采用阻塞IO模型,那么对于每一个请求,Redis需要从接收连接->读取连接数据->处理命令->返回数据整个流程处理完成后才能处理处理下一个请求,如果是这样的话,那么Redis的快就无从谈起了。

而采用非阻塞模型IO,虽然可以避免阻塞,但这种模型会在内核空间与用户空间来回复制全部要监听的FD(文件描述符),除了之外,我们还无法得知哪个FD已经就绪了,需要遍历所有的FD才能知道,同样是即费空间又费时间,因此对于需要支持高并发的Redis来说,显然也是不可接受的。

多路复用IO模型则不同了,多路复用IO模型同样也是非阻塞的,其内置的红黑树可以高效地添加或查找FD,且只会将已经就绪的的FD从内核空间复制到用户空间,因此很多网络应用处理请求时都是使用多路复用IO模型,Redis也不例外。

多路复用IO模型在不同的操作系统有不同的实现,如selec和poll,还有更性能更好的epoll,Redis不同操作系统的多路复用IO模型都有封装。

2.4 避免多线程切换

因为将数据保存在内存中,并且有高效的数据结构和使用多路利用IO模型,所以Redis读取数据非常快速,这时候,你可能会想,是不是将Redis改为多线程,可以更好提升Redis的吞吐量,处理更多并发请求呢?

其实不然,正因为Redis读取非常快,所以如果采用多线程的话,会产生以下两个问题:

  • 多线程切换带来的额外开销:因为Redis读写非常快速,因为如果采用多线程,那么线程切换就非常频繁,所以如果采用多线程的话,Redis大部分时间可能都在切换线程。
  • 数据加锁的性能损耗:多线程访问同一个数据的话,为了避免竞争,肯定需要加锁保证数据操作的原子性,而加锁与等待锁的释放,让多个线程在读取同一个数据需要排队等待,所以效率并不比单线程强多少。

3 怎么让Redis更快

单台Redis服务器,已经非常快了,那么,有什么办法能让Redis更快呢?

  • 使用AOF或RDB数据备份:有了数据备份,可以在服务器宕机时,更快地恢复
  • 主从分离:让从服务器分担主服务器的压力,实现更快地响应
  • 切片集群:除了主从,切片集群让数据分散到不同服务器,多台服务器可分担读写的压力
  • 避免bigkey:bigkey是指键值过大或者集合元素过多,占用太多的带宽与CPU运算,导致主线程阻塞,所以要避免在Redis存储比较大的值,比如对于value来说,不要超过10KB,对于集合元素,元素的数量最好不要超过1000

4 小结

从上面的分析我们可以看,Redis并不是像我们认为的是单线程,在处理持久化等耗时任务时,Redis也是采用多线程的处理方式,不过,Redis在处理请求时只有一个主线程,但仍然做非常快速的响应,这是由于Redis的数据读写都在内存中,而内存的访问是非常快速的,另外Redis为每一种数据类型都精心设计了高效的底层数据结构,而在处理网络请求时,则采用基于多路复用的IO模型,使得Redis可以高并地处理更多的请求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么单线程的Redis那么快? 的相关文章

  • 用于解析和构建逻辑表达式的 Java 库

    我正在寻找一个 Java 开源库来解析和构建类似 SQL 的表达式 例如评估表达式的有效性 例如 a x or y and b z 另外我想要一个用于构建或扩展表达式的 API 就像是 Expression exp new Expressi
  • java.lang.VerifyError:JVMVRFY012堆栈形状不一致;

    在 WAS 8 5 5 中部署 Maven 项目时出现以下错误 我在WAS中安装了JDK 1 6和1 7 错误500 org springframework web util NestedServletException 处理程序处理失败
  • Scala 使用的 Redis 客户端库建议

    我正在计划使用 Scala 中的 Redis 实例进行一些工作 并正在寻找有关使用哪些客户端库的建议 理想情况下 如果存在一个好的库 我希望有一个为 Scala 而不是 Java 设计的库 但如果现在这是更好的方法 那么仅使用 Java 客
  • 如何在 IDEA Intellij 上使用 Spring-boot 自动重新加载

    我写了一个基于Spring boot tomcat freemarker的项目 我运行成功 但是每当我修改一些模板和java类时 我必须重新启动服务器或使用Intellij上的 重新加载更改的类 菜单才能使更改生效 浪费很多时间 然后我尝试
  • 将构造函数作为参数传递给方法

    我是java新手 开始研究构造函数 我看到一些构造函数作为参数传递给方法的示例 请告诉我当构造函数作为参数传递给方法时会发生什么 或者建议我一些链接 我可以在其中获得有关使用构造函数的足够知识 根据您需要传递构造函数的目的 您可以考虑传递供
  • 如何作为应用程序发布到页面?

    所以 我有一个应用程序 Facebook 应用程序实体 并且我有一个页面 我想使用应用程序通过java代码 通过restfb或任何其他建议 发布到页面 看起来我错过了页面授予应用程序发布权限的阶段 不知道该怎么做 谢谢你们 乌里 您只能 作
  • 最快的高斯模糊实现

    如何以最快的速度实施高斯模糊 http en wikipedia org wiki Gaussian blur算法 我要用Java来实现它 所以GPU http en wikipedia org wiki Graphics processi
  • 在java中将字符串日期转换为美国格式

    我有下面的代码 其中日期为字符串类型 我必须将其设置为美国格式 所以下面我已经展示了它 private static final SimpleDateFormat usOutputDate new SimpleDateFormat MM d
  • Java:SortedMap、TreeMap、可比较?如何使用?

    我有一个对象列表 需要根据其中一个字段的属性进行排序 我听说 SortedMap 和 Comparator 是实现此目的的最佳方法 我是否要与正在排序的类实现 Comparable 还是创建一个新类 如何实例化 SortedMap 并传入
  • 插入时的 iBatis 判别器

    我有一个抽象类Example以及与之相伴的具体子类 我使用鉴别器来提取数据out数据库的 像这样
  • 如何从字符串中解析一个大整数? [复制]

    这个问题在这里已经有答案了 我有一个这样的方法 Integer parseInt myInt 不是这个整数变得很长 我得到以下异常 java lang NumberFormatException For input string 40001
  • C 与 C++ 中的 JNI 调用不同?

    所以我有以下使用 Java 本机接口的 C 代码 但是我想将其转换为 C 但不知道如何转换 include
  • 使用单独的线程在java中读取和写入文件

    我创建了两个线程并修改了 run 函数 以便一个线程读取一行 另一个线程将同一行写入新文件 这种情况会发生直到整个文件被复制为止 我遇到的问题是 即使我使用变量来控制线程一一执行 但线程的执行仍然不均匀 即一个线程执行多次 然后控制权转移
  • 如何使用Gson仅从Json反序列化某些特定字段?

    我有以下 JSON 字符串 channel bvmt initValues data value instrumentIds TN0007250012 TN0007500010 instruments mnemonic ADWYA marc
  • javax.media.jai 类的公共下载?

    这是一个非常简单的问题 我一直在寻找可以下载 javax media jai 库的地方 我找到了 jai imageio 库 但是我发现的所有其他 jai 内容要么已经过时 2008 年及之前 然后我遇到了登录屏幕 是否有 javax me
  • Collections.sort(list) 和 list.sort(Comparator) 之间的区别

    有什么理由让我应该选择Collections sort list 方法而不是简单地调用list sort 内部Collections sort只是调用sort的方法List无论如何 上课 令人惊讶的是几乎每个人都告诉我使用Collectio
  • spring data jpa复合键重复键记录插入导致更新

    我有一个具有复合键的实体 我试图通过使用 spring data jpa 存储库到 mysql 数据库来持久化它 如下所示 Embeddable public class MobileVerificationKey implements S
  • 在实现使用原始类型的接口时如何避免警告?

    我正在实施流程工厂 http help eclipse org ganymede index jsp topic org eclipse platform doc isv reference api org eclipse debug co
  • 如何使用自定义 JDK 构建 Jenkins 项目?

    我有一个常规的 Jenkins 实例 运行一些多分支管道 该实例在 JDK 11 上运行 因为 Jenkins 并不真正支持更高版本 没关系 但不好的是 我的所有管道似乎也都受到 Java 11 的限制 Jenkins 仅使用它自己也使用的
  • 如何建立与 FileZilla Server 1.2.0 的 FTPS 数据连接

    使用 Apache commons net 的 Java FTPSClient 进行会话恢复是一个已知问题 会话恢复是 FTPS 服务器数据连接所需的一项安全功能 Apache FTPSClient 不支持会话恢复 并且 JDK API 使

随机推荐

  • 校招算法题实在不会做,有没有关系?

    文章目录 前言 一 校招 二 时间复杂度 1 单层循环 2 双层循环 三 空间复杂度 四 数据结构 五 校招算法题实在不会做 有没有关系 六 英雄算法集训 前言 英雄算法联盟八月集训 已经接近尾声 九月算法集训将于 09月01日 正式开始
  • Mock 模拟测试简介及 Mockito 使用入门

    Mock 是什么 mock 测试就是在测试过程中 对于某些不容易构造或者不容易获取的对象 用一个虚拟的对象来创建以便测试的测试方法 这个虚拟的对象就是mock对象 mock对象就是真实对象在调试期间的代替品 简单的看一张图 我们在测试类 A
  • Java锁机制,synchronized和lock详解。

    Java锁机制详解 1 java各种锁详解 1 1 公平锁 vs 非公平锁 公平锁 是指多个线程按照申请锁的顺序来获取锁 线程直接进入队列中排队 队列中的第一个线程才能获得锁 类似排队打饭 先来后到 非公平锁 是指多个线程获取锁的顺序并不是
  • HDFS的基础详解

    概念 HDFS是Hadoop Distribute File System 的简称 Hadoop 分布式文件系统 是hadoop核心组件之一 作为最底层的分布式存储服务而存在 分布式文件系统解决的问题就是大数据存储问题 设计目标 1 硬件故
  • 自媒体爆款标题怎么写?手把手教你写热门标题

    自媒体内容想提高阅读量 标题是关键 如何创作优质且有吸引的标题呢 自媒体爆款标题怎么写 今天对热门标题进行解析 手把手教你写爆款标题 1 标题贴合热点 用户对热点的关注度超乎想象 有时候对热点关键词的搜索都能让系统崩溃 所以 在标题中添加热
  • Python语言学习实战-内置函数sorted()的使用(附源码和实现效果)

    实现功能 sorted 函数是Python的内置函数之一 用于对可迭代对象进行排序操作 它可以对列表 元组 字符串等可迭代对象进行排序 并返回一个新的已排序的列表 sorted 函数的语法如下 sorted iterable key Non
  • unity用visual studio写代码的时候一直显示importing assets

    项目场景 提示 使用Unity时打开C 弹出opening visual studio 然后一直停在importing assets这个界面 不影响写代码和运行 但是非常不舒服 困扰我许久 特别浪费时间 问题描述 unity3d打开项目中遇
  • JAVA应用程序集成控件JxBrowser v7.2来啦!允许自定义错误页面

    JxBrowser 点击下载 是将基于Chromium的浏览器与Java应用程序集成 以处理和显示HTML5 CSS3 JavaScript Flash等 JxBrowser更新至最新版v7 2 允许针对HTTP和网络错误覆盖标准Chrom
  • ES 搜索7 (多词查询)

    多词查询 如果我们一次只能搜索一个词 那么全文搜索就会不太灵活 幸运的是 match 查询让多词查询变得简单 GET my index my type search query match title BROWN DOG 上面这个查询返回所
  • vue 使用el-option标签解决不回显的问题

    一 问题描述
  • 最新----Dubbo Spring Cloud 重塑微服务治理

    spring cloud alibaba dubbo examples代码示例 摘要 在 Java 微服务生态中 Spring Cloud1 成为了开发人员的首选技术栈 然而随着实践的深入和运用规模的扩大 大家逐渐意识到 Spring Cl
  • 中兴2023秋招二面

    中兴二面 面试时长为20分钟的样子 请问你带了身份证或者学生证件吗 1 自我介绍 姓名 学校 专业 兴趣爱好 2 项目分工 应该问的是如何分工 我回答的不太好 首先根据项目工作量 分为几份工作 团队成员自行根据自己的情况认领对应项目 团队负
  • LU分解法

    LU分解的原理是将一个矩阵分解为一个下三角矩阵L和一个上三角矩阵U的乘积 下面是LU分解的原理 定一个n n的矩阵A 我们希望将它分解为下三角矩阵L和上三角矩阵U的乘积 即A LU 我们可以在矩阵A上进行列主元高斯消元 消元的过程中会用到矩
  • File "errorchecker.pyx", line 17, in OpenGL_accelerate.errorchecker._ErrorChecker.__init__ (src\erro

    先安装 pip install PyOpenGL 3 0 2 再安装 pip install PyOpenGL PyOpenGL accelerate 结果 这次只安装PyOpenGL accelerate 调用代码会报错 File err
  • uView的组件u-picker 选择器

    网址 https www uviewui com components picker html 需要的是数组中的数组 处理核心是将接口获取回来的数组 赋给一个空数组 然后把这空数组再push到一个空数组里面 this arr1 res da
  • docker的配置,基础用法

    什么是docker docker中的容器 lxc gt libcontainer gt runC OCI OCF OCI Open Container initiative 开放容器倡议 由Linux基金会主导于2015年6月创立 旨在围绕
  • 【Swift】LeedCode整数反转

    Swift LeedCode 拿硬币 由于各大平台的算法题的解法很少有Swift的版本 小编这边将会出个专辑为手撕LeetCode算法题 新手撕算法 请包涵 给你一个 32 位的有符号整数 x 返回将 x 中的数字部分反转后的结果 如果反转
  • 小程序分享及返回上级页面

    分享监听 用户点击右上角分享 onShareAppMessage function res console log res if res from menu return title 邀请赢好礼 path pages member memb
  • 【hadoop报错】(ssh)Connection timed out

    背景 在虚拟机上启动hadoop 报错 Starting namenodes on node1 huike cn Last login Thu Oct 14 22 36 08 EDT 2021 on pts 0 node1 huike cn
  • 为什么单线程的Redis那么快?

    1 Redis单线程的本质 其实 Redis并不是单线程 我们之所以会一直称Redis是单线程 这是因为Redis在处理客户端的读写请求时 只有一个主线程 而在处理以下这些操作时 Redis会fork出其他的子线程来处理 主从数据同步 切片