Redis数据结构和类型

news/2024/4/20 16:34:40/文章来源:https://blog.csdn.net/surpass0728/article/details/128108088

Redis 包含五种数据类型,分别为String、List、Hash、Set、ZSet

底层实现的数据结构包SDS、双向链表、压缩列表、哈希表、整数集合、跳表

  • redis结构图

  • 数据类型和数据结构的关系

Redis六种数据结构

一、动态字符串(SDS)

Redis 是用 C 语言实现的,但是它没有直接使用 C 语言的 char* 字符数组来实现字符串,而是自己封装了一个名为简单动态字符串(simple dynamic string,SDS) 的数据结构来表示字符串,也就是 Redis 的 String 数据类型的底层数据结构是 SDS

总的来说,Redis 的 SDS 结构在原本字符数组之上,增加了三个元数据:len、alloc、flags,用来解决 C 语言字符串的缺陷,之所以 SDS 设计不同类型的结构体,是为了能灵活保存不同大小的字符串,从而有效节省内存空间。比如,在保存小字符串时,结构头占用空间也比较少

优点:

  • 获取字符串长度复杂度:C 语言的字符串长度获取 strlen 函数,复杂度是O(n),而 Redis 的 SDS 结构因为加入了 len 成员变量,所以是O(1)
  • 二进制安全:因为 SDS 不需要用 “\0” 字符来标识字符串结尾了
  • 不会发生缓冲区溢出:C 语言的字符串标准库提供的字符串操作函数,大多数(比如 strcat 追加字符串函数)都是不安全的,Redis 的 SDS 结构里引入了 alloc 和 leb 成员变量,这样 SDS API 通过 alloc - len 计算,可以算出剩余可用的空间大小,这样在对字符串做修改操作的时候,就可以由程序内部判断缓冲区大小是否足够用
  • 节省内存空间:SDS 结构中有个 flags 成员变量,表示的是 SDS 类型,之所以 SDS 设计不同类型的结构体,是为了能灵活保存不同大小的字符串,从而有效节省内存空间。比如,在保存小字符串时,结构头占用空间也比较少

二、链表(linkedlist)

list 结构为链表提供了链表头指针 head、链表尾节点 tail、链表节点数量 len、以及可以自定义实现的 dup、free、match 函数

三、压缩列表(ziplist)

压缩列表是 Redis 为了节约内存而开发的,它是由连续内存块组成的顺序型数据结构,有点类似于数组

当我们往压缩列表中插入数据时,压缩列表 就会根据数据是字符串还是整数,以及它们的大小会在 prevlen 和 encoding 这两个元素里保存不同的信息,这种根据数据大小进行对应信息保存的设计思想,正是 Redis 为了节省内存而采用的

压缩列表除了查找复杂度高的问题,压缩列表在插入元素时,如果内存空间不够了,压缩列表还需要重新分配一块连续的内存空间,而这可能会引发连锁更新的问题

压缩列表里的每个节点中的  prevlen 属性都记录了「前一个节点的长度」,而且 prevlen 属性的空间大小跟前一个节点长度值有关,比如:

  • 如果前一个

节点的长度小于 254 字节,那么 prevlen 属性需要用 1 字节的空间来保存这个长度值;

  • 如果前一个

节点的长度大于等于 254 字节,那么 prevlen 属性需要用 5 字节的空间来保存这个长度值

这种在特殊情况下产生的连续多次空间扩展操作就叫做「连锁更新」,连锁更新一旦发生,就会导致压缩列表 占用的内存空间要多次重新分配,这就会直接影响到压缩列表的访问性能

四、哈希表(hash)

Hash 表优点在于,它能以 O(1) 的复杂度快速查询数据。主要是通过 Hash 函数的计算,就能定位数据在表中的位置,紧接着可以对数据进行操作,这就使得数据操作非常快

但是存在的风险也是有,在哈希表大小固定的情况下,随着数据不断增多,那么哈希冲突的可能性也会越高,Redis 采用了链式哈希来解决哈希冲突,以及rehash

为了避免 rehash 在数据迁移过程中,因拷贝数据的耗时,影响 Redis 性能的情况,所以 Redis 采用了渐进式 rehash,也就是将数据的迁移的工作不再是一次性迁移完成,而是分多次迁移

触发 rehash 操作的条件,主要有两个:

  • 当负载因子大于等于 1 ,并且 Redis 没有在执行 bgsave 命令或者 bgrewiteaof 命令,也就是没有执行 RDB 快照或没有进行 AOF 重写的时候,就会进行 rehash 操作。
  • 当负载因子大于等于 5 时,此时说明哈希冲突非常严重了,不管有没有有在执行 RDB 快照或 AOF 重写,都会强制进行 rehash 操作

五、跳表(skiplist)

有序列表 zset 的数据结构,它类似于 Java 中的 SortedSet 和 HashMap 的结合体,一方面它是一个 set 保证了内部 value 的唯一性,另一方面又可以给每个 value 赋予一个排序的权重值 score,来达到 排序 的目的

因为 zset 要支持随机的插入和删除,所以它 不宜使用数组来实现,关于排序问题,我们也很容易就想到 红黑树/ 平衡树 这样的树形结构,为什么 Redis 不使用这样一些结构呢

  1. 性能考虑:在高并发的情况下,树形结构需要执行一些类似于 rebalance 这样的可能涉及整棵树的操作,相对来说跳跃表的变化只涉及局部
  2. 实现考虑:在复杂度与红黑树相同的情况下,跳跃表实现起来更简单,看起来也更加直观;

跳跃表 skiplist 就是受到这种多层链表结构的启发而设计出来的。按照上面生成链表的方式,上面每一层链表的节点个数,是下面一层的节点个数的一半,这样查找过程就非常类似于一个二分查找,使得查找的时间复杂度可以降低到 O(logn)

这种方法在插入数据的时候有很大的问题。新插入一个节点之后,就会打乱上下相邻两层链表上节点个数严格的 2:1 的对应关系。如果要维持这种对应关系,就必须把新插入的节点后面的所有节点 (也包括新插入的节点) 重新进行调整,这会让时间复杂度重新蜕化成 O(n)。删除数据也有同样的问题

skiplist 为了避免这一问题,它不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是 为每个节点随机出一个层数(level)。从上面的创建和插入的过程中可以看出,每一个节点的层数(level)是随机出来的,而且新插入一个节点并不会影响到其他节点的层数,因此,插入操作只需要修改节点前后的指针,而不需要对多个节点都进行调整,这就降低了插入操作的复杂度。

六、整数集合(intset)

intset实质就是一个有序数组,存储元素紧密,空间利用率高,并通过二分法降低查找元素的时间复杂度,而且不容易因频繁地插入删除而产生内存碎片

支持整型编码,intset中所有数据元素的存储类型是一致的。新插入数据时,如果数据的类型大于当前intset的数据类型,为了防止溢出,会对其进行升级操作,然后才能将新元素添加到整数集合里

Intset 只支持升级,不支持降级

升级会引起整个 intset 进行内存重分配,并移动集合中的所有元素,这个操作的复杂度为O(n)

升级整数集合:

1)根据新元素的类型,拓展整数集合底层数组的空间大小,并且为新元素分配空间。

2)将底层数组现有的元素都转成新原属相同的类型,并且将转换后的元素放置到正确的位上,而且放置元素的过程中,需要继续位置数组的有序性质不变。

3)将新元素加入到底层数组里面

Redis五种基本数据类型

一、String

字符串对象的编码可以是int(整数 可以用long类型)、raw(超过39字节)、embstr(小于等于39字节)

raw编码会调用两次内存分配函数来创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的内存,空间依次包含redisObject和sdshdr结构

  • 应用场景
    • 计数器:incr操作用来计数
    • 缓存:缓存普通信息
  • 常用API
set   [key]  [value]   给指定key设置值
get  [key]   获取指定key 的值
setex    [key]  [time]  [value]  等价于 set + expire 命令组合
expire [key]  [time]    给指定key 设置过期时间  单位秒
exists  [key]  判断是否存在指定key
mset  [key1]  [value1]  [key2]  [value2] ...... 批量存键值对
mget  [key1]  [key2] ......   批量取key
incr   [key]           如果value为整数 可用 incr命令每次自增1
incrby  [key] [number]  使用incrby命令对整数值 进行增加 number

二、Hash

Redis 散列可以存储多个键值对之间的映射。和字符串一样,散列存储的值既可以是字符串又可以是数值,并且用户同样可以对散列存储的数字值执行自增或自减操作。这个和 Java 的 HashMap 很像,每个 HashMap 有自己的名字,同时可以存储多个 k/v 对

  • 使用场景
    • Hash更适合存储结构化的数据:存储对象的每个属性
    • 购物车场景:hset [key] [field] [value] 存储购物车的三个要素
    • 用户已读:key:uid field:mid value:时间戳
  • 常用API
hset  [key]  [field] [value]    新建字段信息
hget  [key]  [field]    获取字段信息
hgetall  [key]  获取指定key 字典里的所有字段和值
hmset  [key]  [field1] [value1] [field2] [value2] ......   批量创建

三、List

有序可重复列表,编码可以是ziplist、linkedlist,列表对象保存的所有字符串元素的长度都小于 64 字节并且保存的元素数量小于 512 个,使用 ziplist 编码;否则使用 linkedlist

  • 使用场景
    • 消息队列:rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试
    • 可以用来实现粉丝/点赞列表,通过rpush插入数据,然后使用lrange命令读取最新的元素列表
  • 常用API
rpush  [key] [value1] [value2] ......    链表右侧插入
rpop    [key]  移除右侧列表头元素,并返回该元素
lpop   [key]    移除左侧列表头元素,并返回该元素
llen  [key]     返回该列表的元素个数
lrange [key]  [start_index] [end_index]   获取list 区间内的所有元素 (时间复杂度为 O(n))

四、Set

Redis 的set和list都可以存储多个字符串,他们之间的不同之处在于,list是有序可重复,而set是无序不可重复

  • 使用场景
    • 业务场景用户白名单:点赞、投稿
    • 业务失败兜底留存:用户打赏失败后记录信息
  • 常用API
sadd  [key]  [value]  向指定key的set中添加元素
smembers [key]    获取指定key 集合中的所有元素
scard [key]    获取集合的长度
srem [key] [value]  删除指定元素

五、SortSet

zset也叫SortedSet一方面它是个 set ,保证了内部 value 的唯一性,另方面它可以给每个 value 赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据结构

  • 使用场景
    • 排行榜:score为热度值或者点赞数等 飙升榜
    • 带权重的消息队列:重要的消息 score 大一些,普通消息 score 小一些,可以实现优先级高的任务先执行
    • 投稿审核优先级队列:score为mediaId uuid 发号器,越大说明最新
  • 常用API
zadd [key] [score] [value] 向指定key的集合中增加元素
zrem [key] [value]  删除元素
zrange [key] [start_index] [end_index] 获取下标范围内的元素列表,按score 排序输出
zrevrange [key] [start_index] [end_index]  获取范围内的元素列表 ,按score排序 逆序输出
zrangebyscore [key] [score1] [score2]  输出score范围内的元素列表
zcard [key]  获取集合列表的元素个数
zscore [key] [value] 获取元素的score

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

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

相关文章

在Word、WPS中插入AxMath公式导致行间距异常的解决办法

引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常,如下图所示: 查遍互联网,最有效的办法竟然要取消文档网格对齐,这对于一些严格要求的场合是非常不利的,经过我的尝试&#…

SpringBoot3.0正式发布,我来尝尝鲜

GraalVM 版本:graalvm-ce-java17-22.3.0 SpringBoot3.0 中最重要的特性就是对 GraalVM 的支持,从而达到更快的启动速度,有两种使用方式。 利用 GraalVM 构建可执行文件 因为需要利用 GraalVM 来打包可执行文件,所以需要你的机器上…

Casein-PEG-Indocyanine green 络蛋白-聚乙二醇-吲哚菁绿 Casein-ICG

产品名称:络蛋白-聚乙二醇-吲哚菁绿 英文名称:Casein-PEG-Indocyanine green 质量控制:95% 原料分散系数PDI:≤1.05 存储条件:-20C,避光,避湿 用 途:仅供科研实验使用,…

Ansible 企业级自动化运维实战

一、Ansible 简介 如果Ansible不采用0mq(ZeroMQ),在操作1000个以下的节点性能还可以,如果操作1000个以上的节点,性能就很差。 目前来说Ansible支持local,ssh,0mq,Ansible用ssh来管理被管理主机是最常见的方法。 saltstack简称salt,默认采用0mq(ZeroMQ),支持数万…

TaWRKY19/61/82激活糖转运蛋白TaSTP3从而增强小麦条锈病敏感性

文章信息 题目:Sugar transporter TaSTP3 activation by TaWRKY19/61/82 enhances stripe rust susceptibility in wheat 刊名:New Phytologist 作者:Baoyu Huai,Zhensheng Kang,Jie Liu et al. 单位:Northwest A&…

麒麟信安携手河南IT联盟召开 《麒麟信安信创应用解决方案》线上分享会

在党政及金融、交通、能源等重要行业的信创应用步伐逐步加快的背景下,各行业均面临着不同程度的国产化落地难题。11月29日下午,麒麟信安与河南省信息协会IT产业分会(河南IT联盟)携手召开《麒麟信安信创应用解决方案》线上分享会&a…

ARM汇编之乘法指令

ARM汇编之乘法指令前言 首先,请问大家几个小小问题,你清楚: 乘法指令有哪些种类呢?ARM乘法指令具体的使用场景又有哪些? 今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的…

基础知识java

1.浅克隆和深克隆?深克隆的方法 浅克隆:对象的引用变量只会拷贝地址,不会新建一个对象 深克隆:对象的引用变量也会新建一个对象 实现方式: 浅克隆:实现cloneable接口的clone方法 深克隆:实现Ser…

西门子精彩触摸屏SMART V3组态报警的具体方法示例

西门子精彩触摸屏SMART V3组态报警的具体方法示例 用户自定义报警分为离散量报警和模拟量报警。 离散量报警:离散量对应于二进制数的1位,离散量的两种相反状态可以用1位二进制数的0、1状态来表示。例如:电动机的交流接触器的接通和断开、各种故障信号的出现和消失,都可以用…

flask入门教程之请求与响应

Flask是一个轻量级的web开发框架,依赖jinja2和Werkzeug WSGI服务的一个微型框架。 官方文档:https://flask.palletsprojects.com/en/2.0.x/ 中文文档:http://docs.jinkan.org/docs/flask/ 中文文档的版本会比较低,如果英语OK的话&…

22年-自研-面试题

目录背景题目Activiti回退功能条件分支功能,并行网关、包含网关有没有用到流程流转中,需知会其他人,这些人需同意/做处理(有点流程的感觉),最后所有的意见都要汇总。你的实现思路Redis哪些数据结构&#xf…

【毕业设计】15-基于单片机的交通灯系统设计(原理图+仿真+论文)

【毕业设计】15-基于单片机的交通灯系统设计(原理图、仿真、源代码工程答辩论文答辩PPT) 文章目录【毕业设计】15-基于单片机的交通灯系统设计(原理图、仿真、源代码工程答辩论文答辩PPT)任务书设计说明书摘要设计框架架构设计说明…

【Rust日报】2022-11-29 Wirefish:基于 Tauri 的跨平台数据包嗅探器

Wirefish:基于 Tauri 的跨平台数据包嗅探器作者 stefanodevenuto 通过 Rust Tauri 实现,构建了一个类似 Wireshark 的跨平台数据包嗅探器。这个应用离生产阶段当然还很远,功能和页面上还有很多改善的空间,但是代码组织良好&#…

基于 Hive 的 Flutter 文档类型存储

基于 Hive 的 Flutter 文档类型存储 原文 https://medium.com/gytworkz/document-type-storage-in-flutter-using-hive-a18ea9659d84 前言 长久以来,我们一直使用共享首选项以键对格式在本地存储中存储数据,或者使用 SQLite 在 SQL 数据库中存储数据。 存…

【收藏】安科瑞企业微电网能效管理系统云平台演示账号

安科瑞 李亚俊 Acrel8757 1、AcrelCloud-1000变电所电力运维云平台 网址:https://acrelcloud.cn/ 演示账号:acrel 密码:123456 2、SCADA电力监控系统 网址:http://scada.acrel-eem.com/ 演示账号:acrel 密码:…

Android——使用ContentProvider共享数据

实验名称: 使用ContentProvider共享数据 实验目的: (1)能使用ContentProvider共享数据 (2)能使用内容观察者观察其他程序的数据变化 实验内容及原理&…

Kamiya丨Kamiya艾美捷小鼠血红蛋白ELISA说明书

Kamiya艾美捷小鼠血红蛋白ELISA预期用途: 小鼠血红蛋白ELISA是一种高灵敏度的双位点酶联免疫分析(ELISA)小鼠生物样品中血红蛋白的测定。仅供研究使用。 引言 血红蛋白(HM)是红细胞中的含铁氧转运蛋白。它吸收肺部的…

第10讲:Python列表对象查操作之通过切片获取列表中的元素

文章目录1.切片获取列表中的技术要点1.1切片获取列表中的概念总结1.2.切片的语法格式以及含义3.使用切片方法获取列表中元素3.1.定义一个原始列表列表3.2.当step步长为正数时切片的案例3.3.当step步长为负数时切片的案例3.4.使用负数索引作为切片范围4.将切片后的列表赋值给新的…

【LeetCode】No.103. Binary Tree Zigzag Level Order Traversal -- Java Version

题目链接:https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/ 1. 题目介绍(Binary Tree Zigzag Level Order Traversal) Given the root of a binary tree, return the zigzag level order traversal of its nodes’…

【学习笔记67】JavaScript中的闭包

一、认识函数的过程 1. 定义 在堆内存中开辟一段内存空间(XF001)把函数体的内容,完全百分百的照抄一份,存放在内存空间中(XF001)把内存空间的地址(XF001) 赋值给函数名2. 调用 根据函数名内存储的地址 (XF001) ,去堆内存中找到对应函数会去…