Redis系列-数据结构篇

news2024/10/7 8:22:44

数据结构

string(字符串)

redis的字符串是动态字符串,类似于ArrayList,采用预分配冗余空间的方式减少内存的频繁分配。

struct SDS<T>{
T capacity;
T len;
byte flags;
byte[] content;
}

当字符串比较短时,T可以是byte和short来表示(能省点空间),一个简单的SDS至少占用3字节

struct SDS<T>{
int8 capacity;
int8 len;
int8 flags;
byte[] content;
}

embstr和raw

当字符串比较短时,存储形式embstr;当字符串比较长时,存储形式raw。

暂时无法在文档外展示此内容

struct RedisObject {
int4 type;                   //4bits
int4 encoding;          //4bits
int24 lru;                   //24bits,对象的热度
int32 refcount;         //32bits,记录对象的引用计数,当lru为0时,对象会被销毁
void *ptr;                  //64bits
}

每个对象除了内容以外,RedisObject本身也占用1/2+1/2+3+4+8 = 16字节

如图所示,embstr存储时会把数据结构redisObject和数据SDS挨边存储;raw存储时数据结构RedisObject和SDS时分开存储,通过ptr指针指向SDS的内存地址。

所以一个简单的字符串最少占用3字节+16字节= 19字节

而redis使用的内存分配器jemalloc,tcmalloc分配内存时的单位都是1/4/8/16/32/64,所以假设内存分配给分配了64字节的空间,那么最多给字符串内容的空间只有44字节 = 64 - 3 - 16 - 1(最后一个1是因为字符串需要以null结尾,占一个子节)

扩容

预分配空间大小:capacity

实际字符串长度:len

创建字符串时,len和capacity是一样的长度

当字符串长度小于1M时,扩容都是加倍现有的空间,即

if(len<1024*1024 && len+appendLen>=capacity){
    capacity = 2*capacity;
}

当字符串长度大于1M时,每次扩容比当前多1M空间

if(len>1024*1024 && len+appendLen>=capacity){
    capacity = capacity + 1024*1024;
}
常用操作

set,get,mset,mget,expire,setnx,incr

mset name1 aa name2 bb name3 cc

mget name1 name2 name3

expire name1 5. #5秒后过期

incr age 1

incr age by 5。 #signed long的最大最小值之间

位图

位图来自于字符串的另一种表示,我们知道字符串实际是存储byte数组,redis以此可以单独设置某key第n位为0还是1。通过位图,我们可以节省更多的内存,当然需要配合适当的场景使用

常用操作
零存整取

setbit key index 0/1

get key

整存整取(即字符串)

set key value

get key

整存零取

set key value

getbit key index

零存零取

setbit key value

getbit key index

bitcount:用来统计指定范围内1的个数

bitcount key 0 1 前两个字符(0~1)中1的个数

bitpos:用来查找指定范围内出现的第一个0或1

bitpos key 1 第一个1位

bitpos key 0 第一个0位

bitpos key 1 start end 在start~end范围的字节内,第一个0位

list(列表)

相当于java里面的LinkedList,可以在头部(Left)和尾部(Right)插入/删除

两元素之间使用双向链表连接,可以支持向前向后遍历

encoding:ziplist,quicklist

满足

create if not exists

drop if no elements

常用操作

rpush books python java golang

llen books

lpop books

rpop books

lpush books newBook

lindex books 1 #获取books列表中第2位元素

ltrim books 0 1 #区间内的元素保留,特别的如果后面元素=-1,表示结尾。同理-2表示倒数第二个

lrange books 0 1 #相当于sublist,特别的如果后面元素=-1,表示结尾。同理-2表示倒数第二个

与java中linkedList不同有2

如果redis list中元素比较少时,使用ziplist存储,

如果redis list中元素比较多时,使用ziplist+linkedList存储,即每个“元素”实际上是一个ziplist

问题:为什么redis list中的“元素”是ziplist,不是简单的元素

redis是基于内存的操作,所以对内存的使用要求很苛刻。尽其所能对使用的内存进行优化。如果redis list中元素存储的是简单的元素,那么仅仅元素间的两个指针就占了不少空间,特别是当元素本身占用空间很少时。

为什么元素是ziplist呢?

首先ziplist本身是一个聚集型的列表,通过连续存储以及快速定位,能够提高在ziplist内遍历的效率。同时ziplist内部可以选择压缩深度,也能极大的减少使用的空间

压缩链表

struct ziplist{

int32 zlbytes; //整个压缩链表占用字节数

int32 zltail_offset; //最后一个元素距离压缩列表起始位置的偏移量

int16 zllength; //元素个数

T[] entries; //元素集合

int8 zlend; //压缩链表结束节点,OxFF

}

struct entry{

int prevlen; //前一个entry的字节长度

int encoding; //元素类型编码

optional byte[] content; //元素内容

}

往ziplist里面追加元素

因为ziplist是紧凑型的数据结构,意味着每追加一个元素都需要调用realloc扩展内存,取决于内存分配算法和ziplist当前的内存大小,可能会重新扩展全新的内存,并把原有的拷贝过来,也可能在原来的位置直接扩展

快速链表

无法复制加载中的内容

每个ziplist存储超过8KB就会新起一个ziplist

redis可以选择对quicklist中的每个ziplist进行压缩,以压缩深度表示。默认是压缩深度为0。如果压缩深度等于1,那么收尾两个ziplist不压缩,其他的都压缩。如果压缩深度等于2,那么链表头两个和链表尾两个都不压缩,其他都压缩。

hash(字典)

满足

create if not exists

drop if no elements

与java中的HashMap很相近。都是数组加链表的形式。

encoding:ziplist,hashtable

不同有三:

redis的hash的value只能是字符串,所以需要在业务系统中将其做序列化和反序列化。

redis hash的rehash与HashMap的rehash也不一样。HashMap是一次性将元素进行rehash,如果元素比较多,可能会有卡顿的情况。redis hash中的rehash是渐进式的,发起rehash之后,会通过定时任务或者hash操作指令,渐进式的将旧的hash表的内容拷贝到新hash表中。

redis hash可以对对象的具体属性进行操作,比如

hset books borrowTime 111111

hincr books borrowTime 5

常用操作

hset,hget,hgetall,hmset,hincr,hincrby

set(集合)

满足

create if not exists

drop if no elements

相当于Java里面的HashSet,所有key对应的value都只用一个NULL。且内部的键值是无序的,唯一的

若set中存在某元素,则再次set时,返回0,可以以此判断是否存在元素

encoding:inset,dict(rehash时,通过COW的思维来做)

typedef struct intset {
    uint32_t encoding; // 编码方式,后面会详细解释
    uint32_t length;   // 集合中元素的个数,也就是contents数组的长度
    int8_t contents[]; // 保存元素的数组
} intset;

常用操作

sadd,spop,smembers,sismember,scard(count)

如果set中的元素都是整数并且数据量比较小时,redis会选择使用inset进行数据的存储

struct inset{

int32 encoding;

int32 length;

int content;

}

encoding会有int16,int32,int64三种类型。可以向上扩,不能向下缩。(存储的内容都是整数,并不会占用很大空间,但如果向下缩,就需要额外的内存分配以及原有的内存回收,得不偿失)

zset(有序列表)

满足

create if not exists

drop if no elements

类似于java中SortedSet和HashMap的集合体,一方面可以保证唯一性,另一方面可以为每一个元素添加score,并以此作为排序依据

内部实现是一种叫做“跳跃列表”的数据结构

encoding:ziplist,skiplist

常用操作
zadd books 9.0 “think in java”

zrange books 0 -1 相当于sublist,并且按照score正序列出

zrevrange books 0 -1 相当于sublist,并且按照score倒序列出

zcard books 相当于count

zscore books “think in java” 获取指定value的score

zrank books “think in java” 按score排序后,当前value的排名

zrangebyscore books 0 8.91 在区间之内的所有value

zrangebyscore books -inf 8.91 withscore 在区间之内所有的value+score

zrem books “think in java” 删除value(可以通过这个命令来确保只有一个线程抢到队列(zset)中的)

跳跃列表

其中每根柱子代表一个元素,每个元素可能有多个层级,最左边是表头,它的高度是当前列表最高高度。当要寻找某一个值的时候,从表头最高层开始按照箭头找,只到最底层的节点。表头的score是Double.MIN_VALUE用来垫底

插入元素时,需要按照查找的逻辑找到最底部的节点,然后插入节点,接着会采用随机策略决定新节点有多少层。第一层的概率是100%,第二层的概率是50%,第三层的概率是25%,以此类推,最高可以有64层

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

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

相关文章

在 Vue 项目中,可以通过设置不同的环境变量来区分不同的环境,例如本地开发环境、测试环境和生产环境。以下是设置环境变量的步骤:

1、在src下新建三个文件夹 &#xff08;.env.local、.env.test 和 .env.prod&#xff09; 2、配置信息 .env.local VUE_APP_ENVlocal VUE_APP_API_URLhttp://localhost:8080.env.test VUE_APP_ENVtest VUE_APP_API_URLhttp://124.220.110.203:9090/ .env.prod VUE_APP_…

直播主播产品转场话术

主播产品转场话术 一、预告下一环节 亲爱的观众朋友们&#xff0c;非常感谢大家对我们直播间的关注与支持!现在&#xff0c;我们即将进入下一个环节&#xff0c;这个环节将为您带来更多惊喜!请耐心等待&#xff0c;不要离开哦! 二、解释过渡 感谢大家对我们产品的喜爱与支持…

循环测试之旅——深度解析Pytest插件 pytest-repeat

在软件开发中,测试的重要性不言而喻。而为了提高测试的鲁棒性和可靠性,Pytest插件 pytest-repeat 应运而生。这个插件可以帮助你轻松实现测试用例的循环运行,以更全面地评估代码的稳定性。本文将深入介绍 pytest-repeat 插件的基本用法和实际案例,助你更好地利用循环测试,…

JavaSE基础详细总结

JavaSE基础总结 目录 初始Java数据类型和变量运算符逻辑控制方法数组类和对象继承和多态抽象类和接口String异常 1、初始java Java之父——詹姆斯.高斯林 Java语言的命名&#xff08;爪哇岛。盛产咖啡&#xff09;和咖啡图标都反映了詹姆斯。高斯林热爱咖啡。 1、“一次编…

SpringBoot高级原理

SpringBoot高级原理 今日内容&#xff1a; 理解SpringBoot自动化配置源码理解SpringBoot健康监控 1 SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 目的&#xff1a;通过依赖能了解SpringBoot管理了哪些starter 讲解&#xff1a; 通过依赖 spring-bo…

外汇天眼:新坦利斯曼黄金矿业的前首席执行官承认违反新西兰金融市场法

新西兰金融市场管理局&#xff08;FMA&#xff09;今天确认&#xff0c;新西兰证券交易所上市公司新坦利斯曼黄金矿业有限公司的前首席执行官Matthew Geoffrey Hill承认违反了2013年《金融市场行为法》&#xff08;FMCA&#xff09;&#xff0c;在此法案下作出了虚假和误导性陈…

Linux本地部署SVN服务结合内网穿透实现远程访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

【wink】如何导出Live实况?

1.首页点击「转Live实况」&#xff0c;即可将图片或视频快速转为Live实况格式。 2.编辑后的视频&#xff0c;可在保存分享页点击「存为Live实况」。&#xff08;本功能仅限iOS用户使用&#xff09;

【每日一题】4.LeetCode——杨辉三角

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

佩戴口罩监测识别摄像机

佩戴口罩监测识别摄像机是一种基于计算机视觉技术的智能监测设备&#xff0c;它能够快速准确地识别出人们是否佩戴口罩&#xff0c;并对佩戴口罩的人员进行识别和记录。这种摄像机在当前抗击病毒的背景下具有重要意义&#xff0c;在公共场所、医疗机构、交通枢纽等地方都有着广…

Gradle学习笔记:Gradle的使用方法

文章目录 1.初始化项目2.构建脚本语言选择3.项目命名4.项目构建过程 1.初始化项目 创建一个test空文件夹&#xff0c;在该文件夹下打开终端&#xff0c;并执行命令&#xff1a;gradle init. 会有一个选项让你选择项目的类型。下面是每个选项的含义和用途&#xff1a; basic&am…

cg插画设计行业怎么样,如何学习插画设计

插画设计行业是一个充满创意和艺术性的行业&#xff0c;随着数字化时代的不断发展&#xff0c;cg插画的应用范围越来越广泛&#xff0c;市场需求也在逐年增长。以下是一些关于acg插画设计行业的现状和发展趋势&#xff1a; 市场需求不断增长&#xff1a;随着广告、媒体、影视、…

图像读写(imgcodecs 模块)与视频读写

内置格式 OpenCV 可以在没有任何第三方库帮助的情况下读取以下格式&#xff1a; BMP的 高动态范围 (WITH_IMGCODEC_HDR) 太阳栅格 (WITH_IMGCODEC_SUNRASTER) PPM、PGM、PBM、PFM&#xff08;、WITH_IMGCODEC_PXMWITH_IMGCODEC_PFM) PNG、JPEG、TIFF、WEBP 支持 格式选择…

C语言第九弹---二维数组

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 二维数组 1、二维数组的创建 1.1、二维数组的概念 ​1.2、⼆维数组的创建 2、二维数组的初始化 2.1、不完全初始化 ​2.2、完全初始化 ​2.3、按照行初始化 ​2.4、…

C++实用教程(四):面向对象核心多态 笔记

推荐B站视频&#xff1a;C现代实用教程(四):面向对象核心多态_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV15v4y1M7fF/?spm_id_from333.999.0.0&vd_sourcea934d7fc6f47698a29dac90a922ba5a3 本项目通用的tasks.json文件和launch.json tasks.json {"versi…

TMS智慧园区物流系统:提升化工园区管理效率的最佳选择

在化工行业园区中&#xff0c;企业面临着诸多痛点&#xff0c;如人工管理流程复杂、园区堵车、园区安全管理不到位、设备资产管理缺失、环境管理混乱无章、数据无法追溯等。这些问题不仅影响企业的生产效率和经济效益&#xff0c;还可能对员工的生命安全和环境造成潜在威胁。为…

uniapp封装公共的方法或者数据请求方法

仅供自己参考&#xff0c;不是每个页面都用到这个方法&#xff0c;所以我直接在用到的页面引用该公用方法&#xff1a; 1、新建一个util.js文件 export const address function(options){return new Promise((resolve,reject)>{uni.request({url:"https://x.cxniu.…

C# 命名管道管道NamedPipeServerStream使用

NamedPipeServerStream 是 .NET Framework 和 .NET Core 中提供的一个类&#xff0c;用于创建和操作命名管道的服务器端。命名管道是一种在同一台计算机上或不同计算机之间进行进程间通信的机制。 命名管道允许两个或多个进程通过共享的管道进行通信。其中一个进程充当服务器&…

CentOS网络配置进阶:深入研究network服务和NetworkManager

前言 如果你正在使用CentOS系统,并且想要深入了解网络管理和配置,那么本文肯定适合你!在这篇文章中,作者深入探讨了CentOS中的两种网络管理方式:network服务和NetworkManager。通过详实的讲解和实用的示例,你将会学习到如何使用这两种工具来管理网络接口、配置IP地址、网…

外贸邮件群发软件有哪些?邮件群发的系统?

外贸邮件群发软件哪个比较好&#xff1f;外贸开发信软件推荐&#xff1f; 对于许多外贸企业来说&#xff0c;邮件营销是一种非常有效的推广方式。那么&#xff0c;外贸邮件群发软件就成为了必备的工具。蜂邮EDM将为你揭秘几款主流的外贸邮件群发软件&#xff0c;助你更好地开展…