Redis 为什么这么快?

前言

  作为一名后端软件工程师,工作中你肯定和 Redis 打过交道。但是Redis 为什么快呢?很多人只能答出Redis 因为它是基于内存实现的,但是对于其它原因都是模棱两可。

那么今天就一起来看看是Redis 为什么快吧:

                        Redis 为什么这么快?

一、基于内存实现

  Redis 是基于内存的数据库,那不可避免的就要与磁盘数据库做对比。对于磁盘数据库来说,是需要将数据读取到内存里的,这个过程会受到磁盘 I/O 的限制。而对于内存数据库来说,本身数据就存在于内存里,也就没有了这方面的开销。

二、高效存储结构

  为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对。一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。所以,我们常说,一个哈希表是由多个哈希桶组成的,每个哈希桶中保存了键值对数据。

                                        全局哈希表

  哈希桶中的 entry 元素中保存了key和value指针,分别指向了实际的键和值,因为其value的多样性,哈希表中存储的并不是具体的值,而是一个内存引用地址,在通过内存引用的地址查找到对应的具体的值。这样一来,即使value是一个集合,也可以通过*value指针被查找到。因为这个哈希表保存了所有的键值对,所以,我也把它称为全局哈希表。

  哈希表的最大好处很明显,就是让我们可以用 O(1) 的时间复杂度来快速查找到键值对:我们只需要计算键的哈希值,就可以知道它所对应的哈希桶位置,然后就可以访问相应的 entry 元素。但当你往 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。这其实是因为你忽略了一个潜在的风险点,那就是哈希表的冲突问题和 rehash 可能带来的操作阻塞。

  当你往哈希表中写入更多数据时,哈希冲突是不可避免的问题。这里的哈希冲突,两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。

                      哈希表的哈希冲突

  Redis 解决哈希冲突的方式,就是链式哈希。链式哈希也很容易理解,就是指同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接。

三、单线程避免了上下文前切换

  省去了很多上下文切换的时间以及CPU消耗,不存在竞争条件,不用去考虑各种锁的问题,不存在加锁释放锁操作,也不会出现死锁而导致的性能消耗。

四、使用基于IO多路复用机制的线程模型,可以处理并发的链接。

  Redis采用了epoll 模型进行IO多路复用。Java中也有类似的模型比如NIO,才epoll模型之前还有selector、poll这里不多讲解,epoll模型可以参考下图:

                      epoll 模型

五、渐进式ReHash

  Redis是当然如果这个数组一直不变,那么hash冲突会变很多,这个时候检索效率会大打折扣,所以Redis就需要把数组进行扩容(一般是扩大到原来的两倍),但是问题来了,扩容后每个hash桶的数据会分散到不同的位置,这里设计到元素的移动,必定会阻塞IO,所以这个ReHash过程会导致很多请求阻塞。

  为了避免这个问题,Redis 采用了渐进式 rehash。

  首先、Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2。一开始,当你刚插入数据时,默认使用哈希表 1,此时的哈希表 2 并没有被分配空间。随着数据逐步增多,Redis 开始执行 rehash。

  1、给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍

  2、把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中

  3、释放哈希表 1 的空间

   在上面的第二步涉及大量的数据拷贝,如果一次性把哈希表 1 中的数据都迁移完,会造成 Redis 线程阻塞,无法服务其他请求。此时,Redis 就无法快速访问数据了。

                                                                渐进式rehash

  在Redis 开始执行 rehash,Redis仍然正常处理客户端请求,但是要加入一个额外的处理:

  处理第1个请求时,把哈希表 1中的第1个索引位置上的所有 entries 拷贝到哈希表 2 中

  处理第2个请求时,把哈希表 1中的第2个索引位置上的所有 entries 拷贝到哈希表 2 中

  如此循环,直到把所有的索引位置的数据都拷贝到哈希表 2 中。

  这样就巧妙地把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。

  所以这里基本上也可以确保根据key找value的操作在O(1)左右。

  过这里要注意,如果Redis中有海量的key值的话,这个Rehash过程会很长很长,虽然采用渐进式Rehash,但在Rehash的过程中还是会导致请求有不小的卡顿。并且像一些统计命令也会非常卡顿:比如keys

按照Redis的配置每个实例能存储的最大的key的数量为2的32次方,即2.5亿,但是尽量把key的数量控制在千万以下,这样就可以避免Rehash导致的卡顿问题,如果数量确实比较多,建议采用分区hash存储。

六、缓存时间戳

  我们平常使用系统时间戳时, 常常是不假思索地使用System.currentTimeMillis()或者new Date() .getTime() 来获取系统的毫秒时间戳。但是Redis不能这样做,因为每一次获取系统时间戳都是一次系统调用,而且每次去系统调用是比较费时间的,作为单线程的Redis是无法承受的,所以它需要对于时间戳进行一次缓存,由一个定时任务进行每毫秒更新时间戳,从而获取时间戳都是直接从缓存就取出。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue与vueComponent的关系

创建完组件之后 就会创建一个vueComponent构造函数 当注册成功这个组件并且在页面使用之后 就会创建一个vueComponent实例对象, 所以为了避免组件在使用过程中data对象中的值混乱 组件中的data要写成函数, 使得每次创建的组件实例对象都可以返回一…

批量爬虫采集大数据的技巧和策略分享

目录 1. 使用多线程或异步编程: 2. 设置适当的请求频率: 3. 使用代理服务器: 4. 处理异常和错误: 5. 监控和管理任务队列: 6. 数据存储和处理: 7. 随机化请求参数和头信息: 8. 定时任务…

前端组件库造轮子——Input组件开发教程

前端组件库造轮子——Input组件开发教程 前言 本系列旨在记录前端组件库开发经验,我们的组件库项目目前已在Github开源,下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。 文章旨在总结经验,开源…

大数据风控介绍

众所周知,金融是数据化程度最高的行业之一,也是人工智能和大数据技术重要的应用领域。随着大数据收集、存储、分析和模型技术日益成熟,大数据技术逐渐应用到金融风控的各个环节。个推作为专业的数据智能服务商,拥有海量数据资源&a…

3D姿态相关的损失函数

loss_mpjpe: 计算预测3D关键点与真值之间的平均距离误差(MPJPE)。 loss_n_mpjpe: 计算去除尺度后预测3D关键点误差(N-MPJPE),评估结构误差。 loss_velocity: 计算3D关键点的速度/移动的误差,评估运动的平滑程度。 loss_limb_var: 计算肢体长度的方差,引导生成合理的肢体长度…

【C++】C++ 引用详解 ⑤ ( 函数 “ 引用类型返回值 “ 当左值被赋值 )

文章目录 一、函数返回值不能是 " 局部变量 " 的引用或指针1、函数返回值常用用法2、分析函数 " 普通返回值 " 做左值的情况3、分析函数 " 引用返回值 " 做左值的情况 函数返回值 能作为 左值 , 是很重要的概念 , 这是实现 " 链式编程 &quo…

改进YOLO系列:10.添加NAMAttention注意力机制

添加NAMAttention注意力机制 1. NAMAttention注意力机制论文2. NAMAttention注意力机制原理3. NAMAttention注意力机制的配置3.1common.py配置3.2yolo.py配置3.3yaml文件配置1. NAMAttention注意力机制论文 论文题目:NAM: Normalization-based Attention Module 论文…

【雷达】接收和去噪L波段雷达接收到的信号研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

空时自适应处理用于机载雷达——机载阵列雷达信号环境(Matla代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Neo4j实现表字段级血缘关系

需求背景 需要在前端页面展示当前表字段的所有上下游血缘关系,以进一步做数据诊断治理。大致效果图如下: 首先这里解释什么是表字段血缘关系,SQL 示例: CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status F…

【Mac】编译Spring 源码和Idea导入

今天我们开始Spring源码的阅读之旅。阅读Spring的源码的第一步当然是编译Spring源码。首先我们要去GitHub上将spring源码给clone下来。 笔者编译环境如下: Spring版本:5.28 https://github.com/spring-projects/spring-framework/tree/v5.2.8.RELEASE …

JavaScript——为什么静态方法不能调用非静态方法

个人简介 👀个人主页: 前端杂货铺 🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…

TypeScript三种特殊类型

1.any类型 说明:any类型代表着可以赋值任意类型 let nickname:any"王二"nickname15nicknametruenicknameundefinednicknamenullnickname{}2.unknown类型 说明:类似any类型;只是不能赋值到其它类型上;除了any和known。…

Promise.all和promise.race的应用场景举例

Promise.all( ).then( )适用于处理多个异步任务&#xff0c;且所有的异步任务都得到结果时的情况。 <template><div class"box"><el-button type"primary" plain click"clickFn">点开弹出框</el-button></div> &…

CSS笔记

介绍 CSS导入方式 三种方法都将文字设置成了红色 CSS选择器 元素选择器 id选择器 图中div将颜色控制为红色&#xff0c;#name将颜色控制为蓝色&#xff0c;谁控制的范围最小&#xff0c;谁就生效&#xff0c;所以第二个div是蓝色的。id属性值要唯一&#xff0c;否则报错。 clas…

汤普森采样(Thompson sampling): 理论支持

目录 一、UCB与TS算法数学原理1、Upper Confidence Bounds 数学原理2、Thompson sampling 数学原理a、TS 基本数据原理1. beta 分布2. 共轭分布与共轭先验3. 采样的编程实现 b、TS 算法流程1. TS算法基础版本2. Batched Thompson Sampling 二、UCB与TS算法的优缺点1、TS算法的优…

[管理与领导-49]:IT基层管理者 - 8项核心技能 - 4 - 团队激励

目录 前言&#xff1a; 一、什么是团队激励 二、为什么需要激励 三、激励的误区 3.1 常见误区 3.2 以下是一些常见的激励错误做法&#xff1a; 四、如何正确地激励 五、关于激励的一些理念 六、常见障碍 前言&#xff1a; 管理者存在的价值就是制定目标&#xff0c;即…

Springboot开发所遇问题(持续更新)

SpringBoot特征&#xff1a; 1. SpringBoot Starter&#xff1a;他将常用的依赖分组进行了整合&#xff0c;将其合并到一个依赖中&#xff0c;这样就可以一次性添加到项目的Maven或Gradle构建中。 2,使编码变得简单&#xff0c;SpringBoot采用 JavaConfig的方式对Spring进行配置…

【目标检测】理论篇(2)YOLOv3网络构架及其代码实现

网络构架图&#xff1a; 代码实现&#xff1a; import math from collections import OrderedDictimport torch.nn as nn#---------------------------------------------------------------------# # 残差结构 # 利用一个1x1卷积下降通道数&#xff0c;然后利用一个3x3卷…

接口多态 面试题及习题

基础题目 第一题&#xff1a;概念辨析 什么是接口&#xff0c;如何定义接口&#xff1f; 接口&#xff0c;是Java语言中一种引用类型&#xff0c;是方法的集合。使用interface关键定义接口&#xff0c;其中可以定义抽象方法&#xff0c;默认方法&#xff0c;私有方法&#xf…