当前位置: 首页 > news >正文

redis 存储原理与数据模型

文章目录

  • 一、redis的存储结构
    • 1.1 存储结构
    • 1.2 存储转换
  • 二、字典(dict)实现
    • 2.1 数据结构
    • 2.2 哈希冲突
    • 2.3 扩容
    • 2.4 缩容
    • 2.5 渐进式rehash
    • 2.6 scan 命令
    • 2.7 expire机制
  • 三、跳表(skiplist)实现
    • 3.1 理想跳表
    • 3.2 redis跳表

一、redis的存储结构

1.1 存储结构

在这里插入图片描述

1.2 存储转换

在这里插入图片描述

二、字典(dict)实现

redis 数据库通过 dict 实现映射关系。key 的固定类型是 string,value 的类型有多种。

redis 中 KV 组织是通过字典来实现的;hash 结构当节点超过512 个或者单个字符串长度大于 64 时,hash 结构采用字典实现。

2.1 数据结构

在这里插入图片描述
dict 由哈希表 dictht + 哈希节点 dictEntry 组成。哈希表有两个,通常 ht[0] 使用,ht[1] 不使用;rehash 时,ht[0] 存储 rehash 之前的数据,ht[1] 存储新数据和 ht[0] 迁移来的数据。

// dict相当于C++的类的封装
typedef struct dict {dictType *type;     // dict 类型,封装成员函数void *privdata;     // 私有数据,连接的上下文dictht ht[2];       // 散列表,一个存储当前数据,另一个 rehash 时使用。long rehashidx;     // 指示rehash到哪个位置了,它是从0开始的,如果rehashidx == -1,则rehash未进行。unsigned long iterators; /* number of iterators currently running */
} dict;// 哈希表
typedef struct dictht {dictEntry **table;      // entry 指针数组,保存 entry 的指针unsigned long size;     // 哈希表大小,2的n次幂unsigned long sizemask; // 哈希表掩码 size-1,hash 取余运算优化成位运算unsigned long used;     // 实际存储元素 entry 的个数
} dictht;// 哈希节点
typedef struct dictEntry {void *key; union {void *val;uint64_t u64;int64_t s64;double d;} v;        struct dictEntry *next;
} dictEntry;

1)字符串经过 hash 函数运算得到 64 位整数;
2)相同字符串多次通过 hash 函数得到相同的64位整数;
3)整数对 取余可以转化为位运算;sizemask是size-1,属于对字典的优化。因为散列表的存储是通过hash(key)%size=index确定索引,sizemask是对取余长度的优化,将hash(key)%size变成hash(key) &sizemask,把除法优化为二进制的运算,从而提高执行速度,这种优化的前提是 数组的长度必须是2的n次幂( 2 n 2^n 2n)。

2.2 哈希冲突

哈希冲突指的是不同的键在哈希表中计算得到相同的哈希值,但它们的实际存放位置并不相同。在哈希表中,每个键通过哈希函数映射到一个桶(bucket)或槽(slot),存储在对应的位置上。
在这里插入图片描述
由于哈希表的大小是有限的,而键的数量可能是无限的,所以哈希冲突是不可避免的。

我们通过负载因子 LoadFactor = used / size 来衡量哈希冲突的程度, used 是数组存储元素的个数,size 是数组的长度;
负载因子越小,冲突越小;负载因子越大,冲突越大;redis 的负载因子是 1 .

2.3 扩容

  • 如果负载因子 > 1 ,则会发生扩容;扩容的规则是翻倍;
  • 如果正在 fork (在 rdb、aof 复写以及 rdb-aof 混用情况下)时,会阻止扩容;
  • 但是此时若负载因子 > 5 ,索引效率大大降低, 则马上扩容;这里涉及到写时复制原理;
    在这里插入图片描述
    在写时复制中,当需要修改一个数据副本时,不会立即进行实际的复制操作,而是在修改发生时创建该数据的新副本。这样可以避免对原始数据进行修改,从而保持数据的一致性和完整性。
    写时复制核心思想:只有在不得不复制数据内容时才去复制数据内容;

在这里插入图片描述

2.4 缩容

如果负载因子 < 0.1 ,则会发生缩容;缩容的规则是恰好包含used 的 2 n 2^n 2n
恰好的理解:假如此时数组存储元素个数为 9,恰好包含该元素的就是 ,也就是 16;
在这里插入图片描述
为什么缩容的负载因子不是小于1?
因为缩容的负载因子是小于1的话会造成频繁的扩缩容,扩缩容都有分配内存的操作,内存操作变得频繁就会造成IO密集。

2.5 渐进式rehash

扩容和缩容都会导致rehash,因为映射算法发生了改变。
当 hashtable 中的元素过多的时候,因为redis是一个数据库,里面存储的数据非常多,不能一次性 rehash 到ht[1];这样会长期占用 redis,其他命令得不到响应;所以需要使用渐进式 rehash。

rehash步骤:
将 ht[0] 中的元素重新经过 hash 函数生成 64 位整数,再对ht[1] 长度进行取余,从而映射到 ht[1]。

渐进式规则:
1) 分治的思想,将 rehash 分到之后的每步增删改查的操作当中。
2)在定时器中,最大执行一毫秒 rehash ;每次步长 100 个数组槽位。
3)处理渐进式 rehash 的过程中,不会发生扩容和缩容。

2.6 scan 命令

SCAN命令的引入是为了解决,在某些情况下,需要对Redis数据库中的所有键进行遍历,以便进行某些操作或统计。然而,如果直接使用KEYS命令获取所有键,会对性能产生严重影响,因为KEYS命令会阻塞其他操作,并且在数据集较大时,返回所有键也会消耗大量内存。SCAN命令通过迭代方式,分批次逐步返回匹配的键,避免了一次性返回所有键的问题,从而减少了长时间阻塞的情况。

scan cursor [MATCH pattern] [COUNT count] [TYPE type]

redis在遍历数据期间,如果发生扩容或者缩容,造成映射算法发生改变,键的槽位可能会发生改变。那么继续遍历会发生错误。

因此 scan 采用高位进位加法的遍历顺序,这样 rehash 后的槽位在遍历顺序上是相邻的,对 sacn 那刻起已经存在的元素遍历不会出现重复和遗漏。例外:在scan过程当中,发生两次缩容的时候,会发生数据重复。

在这里插入图片描述

2.7 expire机制

redis的EXPIRE机制用于设置键的过期时间,即在指定时间后自动删除键。它是基于每个键的时间戳实现的。

1)EXPIRE key seconds:设置键 key 的过期时间为 seconds 秒。当键到达过期时间后,Redis会自动删除该键。
2)PEXPIRE key milliseconds:设置键 key 的过期时间为 milliseconds 毫秒。与 EXPIRE 命令类似,但时间单位为毫秒。
3)TTL key:获取键 key 的剩余过期时间(以秒为单位)。如果键不存在或键没有设置过期时间,返回 -1。如果键已过期,返回 -2。
4)PTTL key:获取键 key 的剩余过期时间(以毫秒为单位)。如果键不存在或键没有设置过期时间,返回 -1。如果键已过期,返回 -2。

redis有两种删除方式:
1)惰性删除:分布在每一个命令操作时检查 key 是否过期;若过期删除 key,再进行命令操作。
2)定时删除:在定时器中检查库中指定个数(25)个 key。

需要注意的对大对象(大key)的删除:
在 redis 实例中形成了很大的对象,比如一个很大的 hash 或很大的 zset,这样的对象在扩容的时候,会一次性申请更大的一块内存,这会导致卡顿;如果这个大 key 被删除,内存会一次性回收,卡顿现象会再次产生。
如果观察到 redis 的内存大起大落,极有可能因为大 key 导致的。

# 每隔0.1秒 执行100条scan命令
redis-cli -h 127.0.0.1 --bigkeys -i 0.1

三、跳表(skiplist)实现

跳表的特点

  • 多层级有序链表
  • 最底层包含所有的元素
  • 支持二分查找,快速定位边界,然后在最底层找到范围内所有元素(区别红黑树)。
  • 增删改查的时间复杂度都是 O(log2n)。

3.1 理想跳表

在这里插入图片描述

理想跳表是多层级有序链表,采取空间换时间的方法,每隔一个节点生成一个层级节点,模拟二叉树结构,最底层包含所有的元素。

但是如果对理想跳表结构进行增删操作,很可能改变跳表结构。若重构链表,代价极大。考虑用概率的方法来优化。每次增加节点的时候,1/2 的概率增加一个层级,1/4 的概率增加两个层级,以此类推。经过证明,当数据量足够大(256)时,通过概率构造的跳表趋向于理想跳表,并且此时如果删除节点,无需重构跳表结构,此时依然趋向于理想跳表。时间复杂度为 ( 1 − 1 n c ) × O ( l o g 2 n ) (1-\frac{1}{n^c} )\times O(log_2 n) (1nc1)×O(log2n)

3.2 redis跳表

从节约内存角度出发,redis 考虑牺牲一些时间性能让跳表结构变得更加扁平。以循环双向链表结构实现,每次增加节点时,1/4 的概率增加一个层级,跳表的最高层级为 32。当节点数量大于 128 或者有一个字符串长度大于 64,则使用跳表结构。

比如插入17,先比较第 4 层:(6, nil), 从 6 节点往下跳。比较第 3 层:(6, 25),从 6 节点往下跳。比较第 2 层:(9, 25),从 9 节点往下跳。比较第1层:(12, 19),在 12 节点后插入 节点17。
在这里插入图片描述

#define ZSKIPLIST_MAXLEVEL 32 // 跳表的层级,
#define ZSKIPLIST_P 0.25      // 每个节点增加层级的概率typedef struct zskiplistNode {sds ele;        // 节点存储的数据double score;   // 节点分数,排序使用struct zskiplistNode *backward; // 前一个节点指针struct zskiplistLevel {         // 多级索引数组struct zskiplistNode *forward; // 下一个节点指针unsigned long span;            // 索引跨度} level[];  
} zskiplistNode;typedef struct zskiplist {struct zskiplistNode *header, *tail; // 头尾节点指针unsigned long length;   // 节点数量int level;              // 最大的索引层,默认是1
} zskiplist;

相关文章:

redis 存储原理与数据模型

文章目录 一、redis的存储结构1.1 存储结构1.2 存储转换 二、字典(dict)实现2.1 数据结构2.2 哈希冲突2.3 扩容2.4 缩容2.5 渐进式rehash2.6 scan 命令2.7 expire机制 三、跳表(skiplist)实现3.1 理想跳表3.2 redis跳表 一、redis的存储结构 1.1 存储结构 1.2 存储转换 二、字…...

初识mysql数据库之事务的隔离性

目录 一、理解隔离性 二、隔离级别 1. 不同的隔离级别的简单概述 2. 查看隔离级别 2.1 查看全局隔离级别 2.2 查看会话隔离级别 3. 设置隔离界别 4. 读未提交&#xff08;Read Uncommitted&#xff09; 4.1 读未提交测试 5. 读提交&#xff08;Read Committed&#x…...

今天学学消息队列RocketMQ:消息类型

RocketMQ支持的消息类型有三种&#xff1a;普通消息、顺序消息、延时消息、事务消息。以下内容的代码部分都是基于rocketmq-spring-boot-starter做的。 普通消息 普通消息是一种无序消息&#xff0c;消息分布在各个MessageQueue当中&#xff0c;以保证效率为第一使命。这种消息…...

小程序附件下载并预览功能

一、实现的功能&#xff1a; 1、word、excel、图片等实现下载并预览 2、打开文件后显示文件名称 二、代码&#xff1a; // 判断文件类型whatFileType(url) {let sr url.lastIndexOf("."); // 最后一次出现的位置let fileType url.substr(sr 1); // 截取url的…...

数据库缓存服务——NoSQL之Redis配置与优化

目录 一、缓存概念 1.1 系统缓存 1.2 缓存保存位置及分层结构 1.2.1 DNS缓存 1.2.2 应用层缓存 1.2.3 数据层缓存 1.2.4 硬件缓存 二、关系型数据库与非关系型数据库 2.1 关系型数据库 2.2 非关系型数据库 2.3 关系型数据库和非关系型数据库区别&#xff1a; 2.4 非…...

【雕爷学编程】MicroPython动手做(13)——掌控板之RGB三色灯

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…...

.Net Core上传组件_.Net Core图片上传组件_Uploader7.0

一、.Net Core上传组件Uploader7.0简介 1.当前版本v7.0&#xff0c;前端框架丰富升级 2.前端jquery框架封装,cover.js, 腾讯云cos-js-sdk-v5.min.js 3.后端&#xff0c;支持Asp.Net 和 Asp.Net Core 矿建 4.数据传输模式支持&#xff1a;WebScoket 、Ajax、Form 模式上传到…...

Exadata磁盘损坏导致磁盘组无法mount恢复(oracle一体机磁盘组异常恢复)---惜分飞

Oracle Exadata客户,在换盘过程中,cell节点又一块磁盘损坏,导致datac1磁盘组&#xff08;该磁盘组是normal方式冗余)无法mount Thu Jul 20 22:01:21 2023 SQL> alter diskgroup datac1 mount force NOTE: cache registered group DATAC1 number1 incarn0x0728ad12 NOTE: ca…...

左值引用与右值引用的区别?右值引用的意义?

左值引用与右值引用的区别&#xff1f;右值引用的意义&#xff1f; 1 区别1.1 功能差异1.2 左值引用1.3 右值引用1.3.1 实现移动语义1.3.2 实现完美转发 2 引用的作用3 区分左值和右值3.1 左值3.2 右值 1 区别 左值引用是对左值的引用&#xff1b;右值引用是对右值的引用。 &…...

2023年深圳杯数学建模D题基于机理的致伤工具推断

2023年深圳杯数学建模 D题 基于机理的致伤工具推断 原题再现&#xff1a; 致伤工具的推断一直是法医工作中的热点和难点。由于作用位置、作用方式的不同&#xff0c;相同的致伤工具在人体组织上会形成不同的损伤形态&#xff0c;不同的致伤工具也可能形成相同的损伤形态。致伤…...

Vue的router学习

,前端路由的核心是什么呢&#xff1f;改变URL&#xff0c;但是页面不进行整体的刷新。 vue-router是基于路由和组件的  路由用于设定访问路径, 将路径和组件映射起来&#xff1b;  在vue-router的单页面应用中, 页面的路径的改变就是组件的切换&#xff1b; 使用router需要…...

Inpaint Anything: 自动化抹除视频元素

自动化抹除视频元素 不用逐帧抠图&#xff0c;直接SAM Tracking Video Inpainting就能实现自动化抹除奔跑吧idol。 https://github.com/geekyutao/Inpaint-Anything 目录 网站演示参考文献 网站 https://huggingface.co/spaces/InpaintAI/Inpaint-Anything 演示 原理就是&a…...

Flutter 开发者工具 Android Studio 开发Flutter应用

Flutter 开发者工具 在 Android Studio 开发Flutter应用 &#x1f525; Android Studio 版本更新 &#x1f525; Android Studio Check for Update Connection failed ​ 解决方案 如果是运行的是32位的android studio需要在andriod studio的启动目录下找到studio.exe.vmoptio…...

后端byte[]传给前端接收默认变成string字符串

创建时间&#xff1a;2023.7.28 建议&#xff1a;最好直接用字符串&#xff0c;我是没办法要求保密&#xff0c;存取都是字符串&#xff0c;程序里面是byte数组 既然他到前端会转换成字符串那么就是被转码了 那我们反向转码就好了 这是在后端处理&#xff0c;反正前端也是乱…...

UE5 动画蓝图模板(Animation Blueprint Template)

文章目录 前言准备内容创建动画蓝图使用动画蓝图模板示例1示例2总结前言 本文基于虚幻5.2版本介绍制作动画蓝图模板,本教程要求使用虚幻5.0及以上版本。 准备内容 使用第三人称游戏内容包,已添加可忽略。 选择第三人称游戏,添加到项目。 创建动画蓝图 在 Characters 文件…...

Log4j源码解析

Log4j源码解析 主要流程 Logger logger Logger.getLogger(Main.class); 1、通过Logger.getLogger(Class clazz) 或 Logger.getLogger(String name)进入。 2、加载LogManager进jvm, 执行静态代码块执行初始化, 创建出RepositorySelector实例及LoggerRepository实例(Hierarchy…...

Docker 容器访问宿主机服务

docker 网络简介 docker 在安装时会默认创建三个网络&#xff1a;bridge&#xff08;默认网络模式&#xff09;、 none 、host。 host 直接和宿主机共用网络。bridge 网络隔离&#xff0c;通过虚拟网桥&#xff08;一般是 docker0&#xff09;与宿主机通信。none 禁用网络功能…...

Go 发送邮件

要在Go中发送电子邮件&#xff0c;您可以使用第三方库&#xff0c;如 gomail 。以下是一个使用 gomail 发送电子邮件的示例代码&#xff1a; package main import ("fmt""gopkg.in/gomail.v2" ) func main() {// 创建邮件消息m : gomail.NewMessage()m.Se…...

Spring AOP 的概念及其作用

一、什么是 Spring AOP&#xff1f; 在介绍 Spring AOP 之前&#xff0c;首先要了解一下什么是 AOP &#xff1f; AOP &#xff08; Aspect Oriented Programming &#xff09;&#xff1a;面向切面编程&#xff0c;它是一种思想&#xff0c; 它是对某一类事情的集中处 理 。…...

python基础1——环境安装

文章目录 一、Windows安装二、Linux安装三、pycharm安装3.1 软件安装3.2 个性化设置3.3 基本使用3.3.1 定义变量3.3.2 查看数据类型3.3.3 运算符3.3.4 操作符3.3.5 转义符 一、Windows安装 1、下载软件安装包&#xff0c;官网 2、开始安装。 2.查看是否安装成功。 3.安装…...

uniapp 中 的progress加载进度条 的使用,在 页面显示数据加载的进度条,使用户的使用体验效果更好

学习目标&#xff1a; 学习目标如下&#xff1a; 例如&#xff1a; uniapp 中 的progress加载进度条 的使用&#xff0c;在 页面显示数据加载的进度条&#xff0c;使用户的使用体验效果更好 学习内容&#xff1a; 学习内容如下所示&#xff1a; 相关属性的说明 进度条的显…...

【尚硅谷】第01章:随堂复习与企业真题(Java语言概述)

来源&#xff1a;尚硅谷Java零基础全套视频教程(宋红康2023版&#xff0c;java入门自学必备) 基本都是宋老师发的资料里面的内容&#xff0c;只不过补充几个资料里没直接给出答案的问题的答案。 不想安装markdown笔记的app所以干脆在这里发一遍。 第01章&#xff1a;随堂复习…...

MyBatis的SqlSession理解

SqlSession是Mybatis最重要的构建之一&#xff0c;可以认为Mybatis一系列的配置目的是生成类似JDBC生成的Connection对象的statement对象&#xff0c;这样才能与数据库开启“沟通”&#xff0c;通过SqlSession可以实现增删改查&#xff08;当然现在更加推荐是使用Mapper接口形式…...

axios 某个接口使用自己独有的完整地址

可以在axios请求中使用完整的URL&#xff0c;而不使用baseURL&#xff0c; 只需将url字段设置为完整的URL即可 import axios from axios;export function getInfo() {return axios({url: http://192.168.3.15:8086/test/messages,method: post}); }直接在url字段中提供了完整的…...

WEB:Web_python_template_injection

背景知识 python模板注入 ssit 题目 打开题目&#xff0c;发现页面提示&#xff0c;翻译为python模板注入 先测试是否存在注入 可以发现被执行了 先查看所有的子类 payload {{[].__class__.__base__.__subclasses__()}} 利用site.Printer的os模块执行命令 payload {{.__…...

【Android安全】Embedded Trace Microcell模块

ETM: Embedded Trace Macrocell, hardware unit responsible to generate hardware instruction trace. ETM模块用于在硬件层面实现instruction trace&#xff0c;可用于辅助逆向分析。 使用教程&#xff1a; https://mcuoneclipse.com/2016/11/05/tutorial-getting-etm-inst…...

修改内核驱动之后-如何给内核打补丁

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言思路步骤1.进入下面路径2.修改文件calibrate.c3.使用git工具生产补丁文件4.移动补丁文件到自己的Linux的recipem目录下总结前言 本文来学习如何使用YOCTO修改Linux内核驱动之后,如何通过打补…...

【javaSE】 类和对象详解

目录 面向对象的初步认知 什么是面向对象 面向对象与面向过程 类定义和使用 简单认识类 类的定义格式 注意事项 练习定义类 定义一个狗类 定义一个学生类 注意事项 类的实例化 什么是实例化 注意事项 类和对象的说明 this引用 为什么要有this引用 什么是this引…...

大数据课程D5——hadoop的Sink

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 掌握Sink的HDFS Sink&#xff1b; ⚪ 掌握Sink的Logger Sink&#xff1b; ⚪ 掌握Sink的File Roll Sink&#xff1b; ⚪ 掌握Sink的Null Sink&#xff1b; ⚪ 掌握Si…...

【数据结构】27.移除元素

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …...