redis极速的奥秘
文章目录
- 1.基于内存存储实现
- 2.高效的数据结构
- 3.合理的数据编码
- 4.合理的线程模型
- 5. 虚拟内存机制
- 实现原理
1.基于内存存储实现
内存读写是比在磁盘快很多的,Redis 基于内存存储实现的数据库,相对于数据存在磁盘的 MySQL 数据库,省去磁盘 I/O的消耗。
而使用数据持久化的情况下,Redis会将数据同时保存在内存和磁盘中。
Redis提供了两种持久化方式:RDB(Redis Database)和AOF(Append Only File)。
- RDB持久化方式会定期将内存中的数据快照以二进制形式保存到磁盘上。当Redis重启时,可以通过加载RDB文件将快照数据恢复到内存中。
- AOF持久化方式会将每一条写命令追加到磁盘上的AOF文件中。当Redis重启时,会通过重新执行AOF文件中的所有命令来恢复数据。
无论是RDB还是AOF,都会将数据保存在磁盘上,以保证数据的持久性和可靠性。同时,数据也会保留在内存中,以确保Redis在读取和处理数据时的高性能。
需要注意的是,如果同时开启了RDB和AOF持久化方式,Redis在进行数据恢复时,会优先选择AOF文件进行恢复,因为AOF文件包含了更完整的历史记录。如果AOF文件损坏或不完整,才会使用RDB文件进行恢复。
2.高效的数据结构
MySQL 的索引使用的是B+树,而redis则是由多种数据结构和内部编码
在Redis中,每种数据结构的底层实现数据结构如下:
-
字符串(String):字符串数据结构是简单的动态字符串(SDS),它是一个包含了字符数组和长度信息的结构体。如C语言中字符串会遇到’\0’会结束,SDS标志字符串结束的是len属性。它是对C语言中的字符串的一种封装,而SDS提供了更多的功能和优势。
简单动态字符串具有以下特点:- 动态调整大小:SDS的长度可以根据需要动态地调整,通过记录字符串的长度和分配的空间大小来实现。在进行字符串修改操作时,SDS会根据需要自动扩展或缩小内存空间,避免了频繁的内存分配和拷贝。
- 二进制安全:SDS不仅可以存储文本字符串,还可以存储二进制数据。由于SDS内部维护了字符串的长度信息,所以它可以准确地表示任意二进制数据,而不受到C字符串中使用’\0’字符作为字符串结束符的限制。
- 高效的常数时间操作:SDS支持常数时间的字符串长度获取、追加、删除等操作。这是通过维护字符串的长度信息和使用预分配的空间来实现的。
- 兼容C字符串:SDS可以将C字符串转换为SDS,并且可以直接通过指针访问SDS中的数据。这使得SDS可以与现有的C库函数和代码无缝集成。
-
哈希表(Hash):哈希表的底层实现是字典(Dictionary)。字典是一种用于存储键值对的散列表,采用了哈希算法来实现高效的查找和插入操作。
-
列表(List):列表的底层实现是双向链表(Doubly Linked List)。双向链表是一种每个节点都包含前后指针的数据结构,可以在常数时间内进行节点的插入、删除、访问等操作。
-
集合(Set):集合的底层实现有两种:一种是基于哈希表的实现,另一种是基于有序集合的实现。具体使用哪种实现方式取决于集合的大小和元素类型。
-
有序集合(Sorted Set):有序集合的底层实现是跳跃表(Skip List)和哈希表的组合。跳跃表是一种有序的、快速查询的数据结构,可以按照分数进行有序存储和查询。跳表的特色:
- 高效的查找、快速定位到需要的元素、通过跳过一部分数据,查找操作的平均时间复杂度为O(log N),其中N是元素的数量。这使得跳表在实现有序集合时非常高效,不需要像传统的有序数组那样进行线性搜索。
- 支持范围查找、有序集合中十分重要
3.合理的数据编码
Redis 支持多种数据数据类型,每种基本类型,可能对多种数据结构。什么时候,使用什么样数据结构,使用什么样编码,是 redis 设计者总结优化的结果。
- String:如果存储数字的话,是用 int 类型的编码;如果存储非数字,小于等于39 字节的字符串,是 embstr;大于 39 个字节,则是 raw 编码。
- List:如果列表的元素个数小于 512 个,列表每个元素的值都小于 64 字节(默认),使用 ziplist 编码,否则使用 linkedlist 编码
- Hash:哈希类型元素个数小于 512 个,所有值小于 64 字节的话,使用ziplist 编码,否则使用 hashtable 编码。
- Set:如果集合中的元素都是整数且元素个数小于 512 个,使用 intset 编码,否则使用 hashtable 编码。
- Zset:当有序集合的元素个数小于 128 个,每个元素的值小于 64 字节时,使用ziplist 编码,否则使用 skiplist(跳跃表)编码
4.合理的线程模型
I/O多路复用(IO Multiplexing)是一种同时监视多个I/O流的机制,它允许单个线程同时处理多个I/O操作,提高系统的并发性能。通过使用I/O多路复用,可以避免单线程在处理阻塞式I/O时出现的等待时间,从而提高系统的效率。
在Redis中,I/O多路复用是用来处理客户端连接和网络通信的关键机制。Redis服务器在启动时会创建一个I/O多路复用的事件循环(Event Loop),它负责监听和处理客户端的请求和网络事件。
具体表现在以下几个方面:
- Redis采用非阻塞式I/O模型,通过设置非阻塞套接字(non-blocking socket)来实现异步的I/O操作。这样当一个客户端连接有数据到达时,Redis可以立即进行读取操作,而不会被阻塞等待数据到达。
- Redis使用I/O多路复用机制来同时监听多个客户端连接的事件。通过使用系统调用(如epoll、select、kqueue等),Redis可以将多个套接字注册到事件循环中,并在有事件发生时及时做出相应的处理。
- 当有新的客户端连接建立或已有的连接有数据到达时,Redis的事件循环会触发相应的事件,并调用相应的回调函数来处理。例如,当有新的连接建立时,会调用accept()函数来接受客户端连接;当客户端有数据到达时,会调用read()函数来读取数据。
- Redis使用事件驱动的方式处理客户端请求,将任务交给相应的工作线程进行处理。通过事件循环和线程池的结合,Redis可以高效地处理大量的并发客户端请求,并且保持较低的系统资源消耗。
通过采用I/O多路复用机制,Redis能够高效地处理大量的并发客户端请求,提高系统的吞吐量和响应速度。
但Redis的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。
5. 虚拟内存机制
内存数据库系统,但它也提供了一种虚拟内存机制,允许将数据持久化到磁盘而不仅仅存储在内存中。
虚拟内存机制允许Redis将部分数据从内存中移动到磁盘上,以便处理大于物理内存的数据集。这对于处理大型数据集的情况非常有用,但需要谨慎使用,因为虚拟内存可能导致性能下降。以下是一些关于Redis虚拟内存的关键要点:
-
虚拟内存配置:Redis的虚拟内存可以通过配置文件中的
vm-enabled参数启用或禁用。默认情况下,虚拟内存是禁用的。 -
虚拟内存限制:你可以配置Redis的虚拟内存限制,即
vm-max-memory参数,来指定Redis可以使用的虚拟内存的最大大小。当达到这个限制时,Redis会将一些不常用的数据移到磁盘上,以腾出更多的内存。 -
数据分页:Redis的虚拟内存将数据分为不同的页(pages),每个页的大小由
vm-page-size参数定义。这些页存储在磁盘上,根据数据的访问频率进行移动。 -
数据置换策略:Redis使用一种称为"页置换"的策略来决定哪些数据会被移动到磁盘上。常见的策略包括LRU(最近最少使用)和LFU(最不常使用)。
-
警告和性能影响:使用虚拟内存时,需要注意性能问题。频繁的数据移动和磁盘访问可能会导致性能下降。Redis会在达到一定的内存使用阈值时发出警告,以提醒你。
-
数据持久化:与传统的Redis内存数据库不同,虚拟内存Redis在磁盘上存储数据,因此不需要单独的持久化选项。数据将在Redis服务器重新启动时从磁盘加载到内存中。
实现原理
内存机制的实现原理可以简要概括如下:
-
数据分页:Redis将数据分成固定大小的页面(pages),每个页面的大小由
vm-page-size参数定义。这些页面存储在磁盘上,并根据需要进行加载和卸载。 -
内存映射:Redis使用内存映射(memory mapping)技术来管理虚拟内存。它会将磁盘上的页面映射到进程的地址空间中,这使得可以像访问内存一样访问磁盘上的数据。
-
页置换策略:Redis使用一种页面置换策略来决定哪些数据页应该从内存中移动到磁盘上,以便腾出内存空间。常见的策略包括LRU(最近最少使用)和LFU(最不常使用),可以通过配置文件中的
vm-max-policy参数进行选择。 -
内存管理:Redis会维护一个内存使用情况的统计信息,以了解哪些数据页需要被置换到磁盘上。当内存使用接近虚拟内存限制时,Redis会启动虚拟内存机制,将一些不常用的页面移动到磁盘上,以释放内存。
-
持久化:虚拟内存中的数据页也会被持久化到磁盘上,以便在Redis服务器重新启动时能够恢复数据。这是通过Redis的RDB快照机制来完成的。
需要注意的是,虚拟内存机制在Redis中并不是主要的工作模式,而是一种辅助的机制,用于处理大型数据集。在实践中,虚拟内存通常需要谨慎配置,以避免性能下降。通常情况下,更好的方法是根据实际需求适当地分片数据,或者使用更大的物理内存来容纳数据集,以获得更好的性能。虚拟内存通常用于处理数据集过大,无法完全加载到内存的特殊情况。
相关文章:
redis极速的奥秘
文章目录 1.基于内存存储实现2.高效的数据结构3.合理的数据编码4.合理的线程模型5. 虚拟内存机制实现原理 1.基于内存存储实现 内存读写是比在磁盘快很多的,Redis 基于内存存储实现的数据库,相对于数据存在磁盘的 MySQL 数据库,省去磁盘 I/O…...
three.js之初识three.js
什么是three.js Three.js是一款运行在浏览器中的 3D 引擎(基于WebGL的API的封装) 什么是WebGL? WebGL(英语:Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和Open…...
二维码智慧门牌管理系统:地址管理的现代革命
文章目录 前言一、标准地址的革新二、广泛的应用前景 前言 在科技不断发展和社会进步的背景下,高效、精准、智能的管理系统已经成为当今社会的迫切需求。传统的门牌管理系统在应对这一需求方面已显得力不从心,因此,二维码智慧门牌管理系统的…...
BricsCAD 23 for Mac:轻松驾驭CAD建模的强大工具
如果你正在寻找一款功能强大、操作简便的CAD建模软件,那么BricsCAD 23 for Mac绝对值得你考虑。这款软件将为你提供一套完整的2D和3D设计解决方案,让你在Mac上轻松创建、编辑和修改图形。 一、BricsCAD 23的功能特点 高效的2D和3D建模:Bric…...
如何利用Web应用防火墙应对未知威胁
网络安全是一个永恒的话题,尤其是在未知威胁不断涌现的情况下。Web应用防火墙(WAF)是企业网络安全防线的重要组成部分,能够帮助企业在面对未知威胁时采取有效的防护措施。本文将探讨如何利用Web应用防火墙应对未知的网络威胁。 一…...
四、多线程服务器
1.进程的缺陷和线程的优点 1.进程的缺陷 创建进程(复制)的工作本身会给操作系统带来相当沉重的负担。 而且,每个进程具有独立的内存空间,所以进程间通信的实现难度也会随之提高。 同时,上下文切换(Cont…...
基于vue实现滑块动画效果
主要实现:通过鼠标移移动、触摸元素、鼠标释放、离开元素事件来进行触发 创建了一个滑动盒子,其中包含一个滑块图片。通过鼠标按下或触摸开始事件,开始跟踪滑块的位置和鼠标/触摸位置之间的偏移量。然后,通过计算偏移量和起始时的…...
探寻蓝牙的未来:从蓝牙1.0到蓝牙5.4,如何引领无线连接革命?
►►►蓝牙名字的来源 这要源于一个小故事,公元940-985年,哈洛德布美塔特(Harald Blatand),后人称Harald Bluetooth,统一了整个丹麦。他的名字“Blatand”可能取自两个古老的丹麦词语。“bla”意思是黑皮肤的,而“tan…...
openssl 之 RSA加密数据设置OAEP SHA256填充方式
背景 如题 环境 openssl 1.1.1l c centos7.9 代码 /** 思路:填充方式自己写,不需要使用库提供的,然后加密时选择不填充的方式加密 关键代码 */ int padding_result RSA_padding_add_PKCS1_OAEP_mgf1(buf, padding_len, (unsigned char*…...
js将带标签的内容转为纯文本
背景:现需要将富文本的所有 html 标签全部删除得到纯文本 思路:创建临时DOM元素并获取其中的文本 创建一个临时 DOM 并给他赋值,然后我们使用 DOM 对象方法提取文本。 代码如下: convertToPlain( html){//新创建一个 divvar di…...
如何通过内网穿透实现远程连接NAS群晖drive并挂载电脑硬盘?
文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…...
4.2 抽象类
1. 抽象类概念 定义一个类时,常常需要定义一些成员方法用于描述类的行为特征,但有时这些方法的实现方式是无法确定的。例如,Animal类中的shout()方法用于描述动物的叫声,但是不同的动物,叫声也不相同,因此…...
ITextRenderer将PDF转换为HTML详细教程
引入依赖 <dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.1.18</version></dependency> 问题一:输出中文字体 下载字体simsun.ttc 下载链接&am…...
c#设计模式-行为型模式 之 备忘录模式
🚀简介 备忘录模式(Memento Pattern)是一种行为型设计模式,它保存一个对象的某个状态,以便在适当的时候恢复对象。所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象…...
ffmpeg+安卓+yolo+RK3399部署
一次满足多项需求. 首先, 思路是, 使用ffmpeg解码本地mp4文件, 在无需任何其他改动的情况下, 就可以直接播放rtsp流, 这个是使用ffmpeg的好处. ffmpeg本身是c语言的, 所以需要编译成jni的库, https://note.youdao.com/s/6XeYftc 具体过程在这里, 用windows/macOS, Ubuntu应该都…...
发电机教程:小白必学的柴油发电机技巧
柴油发电机监控是关键的能源管理和维护工具,它用于确保持续的电力供应,提高能源效率,并延长发电机的寿命。 随着科技的不断发展,监控系统变得更加智能和高效,使用户能够远程监测和管理柴油发电机的运行状态。 客户案例…...
基础课1——人工智能的分类和层次
1.人工智能的分类 人工智能(AI)的分类主要有以下几种: 弱人工智能(Artificial Narrow Intelligence,ANI):弱人工智能是擅长于单个方面的人工智能,例如战胜象棋世界冠军的人工智能阿…...
C语言复杂表达式与指针高级
一、指针数组与数组指针 1.指针数组VS数组指针 (1)指针数组:实质是一个数组,因为这个数组中传参的内容全部是指针变量。 (2)数组指针:实质是一个指针,这个指针指向一个数组 2.分析指…...
【Python从入门到进阶】39、使用Selenium自动验证滑块登录
接上篇《38、selenium关于Chrome handless的基本使用》 上一篇我们介绍了selenium中有关Chrome的无头版浏览器Chrome Handless的使用。本篇我们使用selenium做一些常见的复杂验证功能,首先我们来讲解如何进行滑块自动验证的操作。 一、测试用例介绍 我们要通过sel…...
利用FPGA和CPLD数字逻辑实现模数转换器
数字系统的嵌入式工程师熟悉如何通过使用FPGA和CPLD在其印刷电路板上将各种处理器,存储器和标准功能组件粘合在一起来实现其数字设计的“剩余”。除了这些数字功能之外,FPGA和CPLD还可以使用LVDS输入,简单的电阻电容器(RC…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
