redis底层数据结构

总所周知,redis支持五种数据类型String、Hash、List、Set、ZSet。在支持这些复杂数据结构的同时,redis不仅需要保证读写的性能,还能提供各种微操作,比如直接修改Hash字典中的某个field的值,或者直接往ZSet中插入某个值,redis也能快速地放到它对应顺序的位置,那么redis是如何做到的呢。
首先上面所说的操作其实分两步,第一步基于key找到对应的value,即整个hash字典对象或整个ZSet的对象,然后第二步再在hash字典中找到对应的field,或者在ZSet中按权重顺序找到对应的位置插入数据。
上面两步也是基于两个不同的数据结构来实现快速访问的,第一步是基于全局hash表来实现的,所有redis的key->value都是通过这样的方式。第二步则有很多种情况,底层的数据结构有这么些种类:SDS(simple dynamic string)简单动态字符串、字典dict、压缩列表ziplist、快速列表quickList、inset、跳跃表skiplist,对外表现的五种数据类型的底层会对应地使用其中的一种或两种来实现。

一、key的存储结构:全局Hash表

Key的存储结构:全局hash表,读写检索到对应key的位置的时间复杂度都是O(1)。再通过rehash避免key的hash冲突,保证不会存在链表过长的情况导致检索性能下降。
rehash参考:《Redis中Rehash浅析》
在这里插入图片描述

二、Value存储结构

1. SDS(simple dynamic string)

redis中默认的字符串表示,key和String类型的value都是用这个数据结构。有几个优点:

  1. len字段保证了获取字符串长度时,时间复杂度是O(1),不用遍历计数。
  2. 空间可以预分配,开辟空间或者字符串变更需要增加空间时,需要字符串长度len小于1M时,预分配空间free长度=len。len大于1M时,free=1M。
  3. 空间惰性释放,当字符串长度变小时,不立即回收内存,而是只调整len和free的大小。
  4. 二进制安全,针对一些二进制文件,可能包含\0符号,SDS不以\0为字符串结束符判断,而是len+\0作为字符串是否结束判断。
  5. 如果一个String类型的value的值是数字,那么Redis内部会把它转成long类型来存储,从而减少内存的使用。
//SDS数据结构
struct sdshdr{//记录buf数组中已使用字节的数量//等于 SDS 保存字符串的长度int len;//记录 buf 数组中未使用字节的数量int free;//字节数组,用于保存字符串char buf[];
}

为了节省内存空间,Redis 还做了如下优化:
当保存 Long 类型整数,RedisObject 中的指针直接赋值为整数数据,这样就不用额外的指针指向整数。这种方式称为 int 编码方式。
当保存字符串数据,且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样可以避免内存碎片。这种方式称为 embstr 编码方式。
当保存字符串数据,且字符串大于 44 字节时,Redis 不再把 SDS 和 RedisObject 放在一起,而是给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种方式称为 raw 编码模式。
下图为 int、embstr 和 raw 这三种编码模式的对比:
在这里插入图片描述

2. 字典dict

字典dict就是类似hashmap key-value的方式,也就是数组+链表的方式。
在这里插入图片描述
在这里插入图片描述

typedef struct dict {// 类型特定函数dictType *type;// 私有数据void *privdata;// 哈希表dictht ht[2];// rehash 索引// 当 rehash 不在进行时,值为 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */
} dict;
typedef struct dictht {// 哈希表数组dictEntry **table;    // 哈希表大小unsigned long size;// 哈希表大小掩码,用于计算索引值// 总是等于 size - 1unsigned long sizemask;// 该哈希表已有节点的数量unsigned long used;
} dictht;
typedef struct dictEntry {// key:键void *key;// v:值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点,形成链表struct dictEntry *next;
} dictEntry;

3. ziplist压缩列表

压缩列表ziplist是一块连续的内存空间,元素之间紧挨着存储,没有任何冗余空隙。元素的检索定位是通过偏移量来完成的。
压缩列表为了支持双向遍历,所以才会有 ztail_offset 这个字段,用来快速定位到最后一 个元素,然后倒着遍历。

//ziplist数据结构
struct ziplist<T> {int32 zlbytes; // 整个压缩列表占用字节数int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点int16 zllength; // 元素个数T[] entries; // 元素内容列表,挨个挨个紧凑存储int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}
//entry数据结构
struct entry {int<var> prevlen; // 前一个 entry 的字节长度,当压缩列表倒着遍历时,需要通过这个字段来快速定位到下一个元素的位置int<var> encoding; // 元素类型编码optional byte[] content; // 元素内容
}

4. 快速列表quickList

快速列表就是一个一个小的压缩列表串起来的双向链表, quicklist【quicklist = 链表+ziplist】

  1. quickList就是一个标准的双向链表的配置,有head 有tail;
  2. 每一个节点是一个quicklistNode,包含prev和next指针。
  3. 每一个quicklistNode 包含 一个ziplist,*zp 压缩链表里存储键值。
  4. 所以quicklist是对ziplist进行一次封装,使用小块的ziplist来既保证了少使用内存,减少附加的指针空间,并减少内存的碎片化,也保证了性能。

在这里插入图片描述
5. 整数数组inset

typedf struct inset{uint32_t encoding;//编码方式 有三种 默认 INSET_ENC_INT16uint32_t length;//集合元素个数int8_t contents[];//实际存储元素的数组 //元素类型并不一定是ini8_t类型,柔性数组不占intset结构体大小//并且数组中的元素从小到大排列
}inset;

编码格式encoding:共有三种,INTSET_ENC_INT16、INSET_ENC_INT32和INSET_ENC_INT64三种,分别对应不同的范围。Redis为了尽可能地节省内存,会根据插入数据的大小选择不一样的类型来进行存储。会且只在必要的时候进行升级操作,节省内存,升级过程耗费系统资源,还有就是不支持降级,一旦升级就不可以降级
元素数量length:记录了保存数据的数组contents中共有多少个元素,这样获取个数的时间复杂度就是O(1)。
数组contents:真正存储数据的地方,数组是按照从小到大有序排列的,并且不包含任何重复项。
在这里插入图片描述

6. 跳跃表skiplist

跳跃表的逻辑类似于“树”型结构,是将一个链表其中的元素间隔几个就向上抽取一层,这样实现检索的过程时间复杂度达到O(logN)。

typedef struct zskiplist {struct zskiplistNode *header, *tail;//跳表节点 ,头节点 , 尾节点unsigned long length;//节点数量int level;//目前表内节点的最大层数
} zskiplist;typedef struct zset {dict *dict;zskiplist *zsl;
} zset;typedef struct zskiplistNode{struct zskiplistLevel{struct zskiplistNode *forward; // 前进指针unsigned int span;	// 跨度 这个层跨越的节点数量} level[];struct zskiplistNode *backward;// 后退指针double score;// 分值robj *obj;// 成员对象
} zskiplistNode;

在这里插入图片描述
在这里插入图片描述
Redis跳跃表常用操作的时间复杂度:
在这里插入图片描述

三、五种数据分别对应的数据结构

在这里插入图片描述

从上图可以看出除了string,其它四种类型在底层实现上都有两种选择。

  1. String只有一个中数据类型:SDS,但是可以选择整数的编码方式方便计数类操作。
  2. Hash的底层实现中,当数据量较小的时候,采用zipList作为hash的底层实现,否则使用字典dict来实现的。使用压缩列表的满足条件是:
    ①哈希对象保存的所有键值对的键和值的字符串长度都小于64字节。
    ②哈希对象保存的键值对数量小于512个
  3. list底层数据结构ziplist和quicklist,首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist ,即压缩列表 . 它将所有的元素紧挨着一起存储,分配的是一块连续的内存当数据量比较多才会改成 quicklist。
  4. set底层数据结构inset和dict,当value是整数值时,且数据量不大时使用inset来存储,其他情况都是用字典dict来存储。
  5. zset底层数据结构压缩列表ziplist和跳表skiplist,有两个配置来判断使用哪一个数据结构:
    ①zset-max-ziplist-entries 128:zset采用压缩列表时,元素个数最大值。默认值为128。
    ②zset-max-ziplist-value 64:zset采用压缩列表时,每个元素的字符串长度最大值。默认值为64。
    zset插入第一个元素时,会判断下面两种条件,zset-max-ziplist-entries的值是否等于0;zset-max-ziplist-value小于要插入元素的字符串长度,满足任一条件Redis就会采用跳跃表作为底层实现,否则采用压缩列表作为底层实现方式。一般情况下,不会将zset-max-ziplist-entries配置成0,元素的字符串长度也不会太长,所以在创建有序集合时,默认使用压缩列表的底层实现。zset新插入元素时,会判断以下两种条件:zset中元素个数大于zset_max_ziplist_entries;插入元素的字符串长度大于zset_max_ziplist_value。当满足任一条件时,Redis便会将zset的底层实现由压缩列表转为跳跃表。转换为跳跃表后,即使元素被逐渐删除,也不会重新转为压缩列表。

参考:一、redis原理之string底层数据结构SDS
二、redis原理之hash底层数据结构ziplist dict
三、redis原理之list底层数据结构
四、redis原理之set底层数据结构
五、redis原理之sort set底层数据结构
Redis的五种数据结构的底层实现原理

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

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

相关文章

Failed to start The nginx HTTP and reverse proxy server.

本章教程主要分享一下&#xff0c;当nginx 启动时&#xff0c;遇到报这个错误时的一个解决问题思路。 目录 1、观察报错信息 2、尝试性解决 1、观察报错信息 根据日志的信息&#xff0c;我们至少可以知道2个比较信息。 1、操作用户执行命令是在非root权限下进行操作的。 2、Ad…

Xcode14创建github远程仓库Token

1.点击Create a Token on GitHub 2.在打开的网页中,登陆GitHub 3.点击生成Token 这是不能为空 4.Token创建成功如下: 5.复制Token到Xcode然后点击Sign In登陆 正在创建远程我仓库 正在将本地仓库代码推入远程仓库 创建成功

C++项目——云备份-②-第三方库认识

文章目录 专栏导读1. json 认识1.1 JSON 数据结构的特点 2. jsoncpp库认识3. json实现序列化案例4. json实现反序列化案例5. bundle文件压缩库认识6. bundle库实现文件压缩案例7.bundle库实现文件解压缩案例8.httplib库认识9. httplib库搭建简单服务器案例10. httplib库搭建简单…

YOLO目标检测——密集人群人头检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;在公共场所&#xff0c;如车站、商场、景区等&#xff0c;可以通过人头目标检测技术来监测人群流量数据集说明&#xff1a;人头检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注…

python实现excel的数据提取

一文带你实现excel表格的数据提取 今天记录一下如何使用python提取Excel中符合特定条件的数据 在数据处理和分析的过程中&#xff0c;我们经常需要从Excel表格中提取特定条件下的数据。Python的pandas库为我们提供了方便的方法来进行数据查询和过滤。 Pandas 是 Python 语言…

SELECT COUNT(*) 会造成全表扫描吗?

前言 SELECT COUNT(*)会不会导致全表扫描引起慢查询呢&#xff1f; SELECT COUNT(*) FROM SomeTable 网上有一种说法&#xff0c;针对无 where_clause 的 COUNT(*)&#xff0c;MySQL 是有优化的&#xff0c;优化器会选择成本最小的辅助索引查询计数&#xff0c;其实反而性能…

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸。 AI领域人才辈出,突然就跳出一个大佬“s0md3v”,开源了一个单图就可以进行视频换脸的项目。 项目主页给了一张换脸动图非常有说服力,真是一图…

Fiber Golang:Golang中的强大Web框架

揭示Fiber在Go Web开发中的特点和优势 在不断发展的Web开发领域中&#xff0c;选择正确的框架可以极大地影响项目的效率和成功。介绍一下Fiber&#xff0c;这是一款令人印象深刻的Golang&#xff08;Go语言&#xff09;Web框架。以其飞快的性能和强大的特性而闻名&#xff0c;…

【算法训练-动态规划 一】【应用DP问题】零钱兑换、爬楼梯、买卖股票的最佳时机I、打家劫舍

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【动态规划】&#xff0c;使用【数组】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&…

VR虚拟现实技术在法院技能培训中的应用

开展法治宣传教育&#xff0c;是全面贯彻落实科学发展观的重要决策&#xff0c;也是保障和促进经济设备会发展和实施“十一五”规划的内在要求。为了让全民法治文化宣传深入人们群众中&#xff0c;突破性地采用VR虚拟现实、web3d开发和三维仿真技术&#xff0c;开发线上法治文化…

大数据技术学习笔记(二)—— Hadoop运行环境的搭建

目录 1 准备模版虚拟机hadoop1001.1 修改主机名1.2 修改hosts文件1.3 修改IP地址1.3.1 查看网络IP和网关1.3.2 修改IP地址 1.4 关闭防火墙1.5 创建普通用户1.6 创建所需目录1.7 卸载虚拟机自带的open JDK1.8 重启虚拟机 2 克隆虚拟机3 在hadoop101上安装JDK3.1 传输安装包并解压…

<蓝桥杯软件赛>零基础备赛20周--第1周

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列。 每个周末发1个博客&#xff0c;共20周&#xff0c;到明年3月初结束。跟上本博客的节奏&#xff0c;省赛三等奖跑不掉。 每周3…

Nvidia显卡基础概念介绍

一、PCIe与SXM 1.1 Nvidia GPU PCIe PCIe(peripheral component interconnect express)是一种高速串行计算机扩展总线标准&#xff0c;是英特尔公司在2001年提出来的&#xff0c;它的出现主要是为了取代AGP接口&#xff0c;优点就是兼容性比较好&#xff0c;数据传输速率高、…

Zabbix告警与飞书集成

一、配置媒介 1、下载飞书的Zabbix媒介类型如下&#xff1a; zbx_export_mediatype_feishu.xml 2、Zabbix中导入媒介类型 Zabbix Web中选择管理 > 报警媒介&#xff0c;然后导入该媒介类型。导入规则选择“更新现有的”和“创建新的”。 3、配置飞书媒介类型用户 Zabbi…

从一个webpack loader中学习

chalk&#xff1a;给终端输出加一些自定义的样式 loader-utils&#xff1a;webpack的loader配置中会通过options传入一些用户自定义参数&#xff0c;就可以通过该包提供的getoptions()获取 node-fetch&#xff1a;Node.js的模块&#xff0c;用于从远程服务器获取数据 关于bab…

口袋参谋:如何一键获取竞品数据?这招实用!

​在淘宝天猫上开店&#xff0c;市场竞争日益激烈&#xff0c;想要做好店铺&#xff0c;我们就不得不去分析竞品的数据了。 很多卖家开店后&#xff0c;一上来就直接卡在类目前10&#xff0c;折腾了一两个月才发现自己对标错了对象&#xff0c;最终竹篮打水一场空。 所以&…

uni-app:实现时钟自走(动态时钟效果)

效果 核心代码 使用钩子函数 mounted()&#xff0c;设置定时器&#xff0c;是指每秒都要去执行时间的获取&#xff0c;以至于实现时间自走的效果 mounted() { this.updateTime(); // 初始化时间 setInterval(this.updateTime, 1000); // 每秒更新时间 }, 自定义方法…

Ubuntu22.04系统 Cgroup v2 切换成v1

使用v1导致docker容器启动失败 Failed to mount cgroup at /sys/fs/cgroup/systemd: Operation not permitted Issue #4072 lxc/lxc GitHub https://github.com/lxc/lxc/issues/4072 原因&#xff1a;ubuntu自21.04版本后的版本&#xff08;不包含21.04&#xff09;linux内…

QT判断平台和生成版本设置输入目录

QT判断平台和生成版本设置输入目录 pro工程文件中常用的宏定义Chapter1 QT判断平台和生成版本设置输入目录Chapter2 Qt pro文件中判断 x86/arm(aarch64)交叉编译环境&#xff0c;区分 linux/windows系统, debug/release版本Chapter3 Qt的版本判断、跨平台选择与pro工程文件输出…

2015款MacBook Pro从Big Sur升级到Monterey

机器信息 存储是1TB的固态硬盘。 升级后的使用体验 开机速度 比之前Big Sur系统开机时间快了至少三分之一&#xff08;进入系统的进度条停顿时间很短&#xff0c;未升级之前&#xff0c;进度条加载缓慢&#xff0c;动不动就停顿半天&#xff09; 应用app使用情况 从Big Su…