redis底层数据结构 -String

article/2025/9/13 21:57:25

redis包含5种常用数据结构

String 、List、Hash、Set 、Zset

基础铺垫

以set为例 

redis其实可以理解为 K-V数据库,因此对每个键值对都会有一个 dictEntry,里面存储了指向 Key 和 Value 的指针;next 指向下一个 dictEntry,与本 Key-Value 无关

key

可以看出 key不是直接存的字符串,而是一个SDS结构 

 value

 value既不是存的String,也不是存的SDS结构,而且用的RedisObject结构   

RedisObject

实际上,不论 redis存储的值 是 5 种类型的哪一种,都是通过 RedisObject 来存的; 

  1. RedisObject 中的 type 字段指明了 要存的值的类型,即:String/List/Set/Zset/Hash中的一个;
  2. RedisObject 中的encoding表示底层使用的编码格式,为了提供存储效率和执行效率,每种数据类型的底层结构不止一种
  3. RedisObject 中的ptr 字段则指向对象所在的地址。可以看出,字符串对象虽然经过了 RedisObject 的包装,但仍然需要通过 SDS 存储。

所以,每种类型都是通过设置RedisObject中的这三个属性来实现存值的。

SDS结构与其他语言字符串结构比较

      1.获取字符串长度复杂度

由于SDS结构中含有len属性,所以获取字符串长度的复杂度为o(1);而对于其他语言来说,获取字符串的长度需要遍历字符串计数来实现,时间复杂度o(n);

     2.字符串的内存重分配次数

其他语言由于不记录字符串长度,所以要修改字符串必须重新分配内存。SDS实现了空间预分配和惰性释放两种策略:

  • 空间预分配:当SDS的API 对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,不仅会为SDS分配修改所需的空间,还会为SDS额外分配空间,这样可以减少连续执行字符串增长操作所需内存分配次数。
  • 惰性释放:当 SDS 的 API 需要对 SDS 保存的字符串进行缩短时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用 free 属性将这些字节的数量记录起来,并等待将来使用,

     3.二进制安全

二进制安全(binary-safe):指能处理任意的二进制数据,包括非 ASCII 和 null 字节。C 字符串以空字符 '\0',作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串'\0',导致程序读入的空字符会被误认为是字符串的结尾,因此C字符串无法正确存取二进制数据;SDS 的 API 都是以处理二进制的方式来处理 buf 里面的元素,并且 SDS 不是以空字符串'\0'来判断是否结束,而是以 len 属性表示的长度来判断字符串是否结束,因此 Redis 不仅可以保存文本数据,还可以保存任意格式的二进制数据。

    4.C字符串函数兼容

SDS 的buf数组会以'\0'结尾,这样可以重用 C 语言库<string.h> 中的一部分函数,避免了不必要的代码重复。

    5.API安全性与缓冲区溢出

缓冲区溢出(buffer overflow):会存在这样的一种异常,当程序将数据写入缓冲区时,会超过缓冲区的边界,并覆盖相邻的内存位置。在 C 语言中使用 strcat 函数来进行两个字符串的拼接,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出

由于 SDS 记录了自身长度,同时在修改时,API 会按照如下步骤进行:

(1)先检查SDS的空间是否满足修改所需的要求;

(2)如果不满足要求的话,API 会自动将 SDS 的空间扩展至执行修改所需的大小;

(3)然后才执行实际的修改操作;

所以SDS不会造成缓冲区溢出情况

一、String

 字符串存储过程分为两步:1、选择合适的SDS类型;2、选择合适的encoding编码格式;

1、选择合适的SDS类型

根据值value的长度选择对应的SDS类型

源码分析

根据位移计算可知 1<<8 = 2^8=256,单位是字节。也就是说,每种类型的SDS可存储的字节数如下:

SDS_TYPE_5 -- 32 Byte
SDS_TYPE_8 -- 256 Byte
SDS_TYPE_16 -- 64KB
SDS_TYPE_32 -- ...
SDS_TYPE_64 -- ...
        

2、选择合适的encoding编码格式

Redis的全部底层数据结构有:

redis_encoding_int (long类型的整数)

redis_encoding_embstr  (embstr编码的简单动态字符串)

redis_encoding_raw (简单动态字符串)

redis_encoding_ht (字典)

redis_encoding_linkedlist (双端链表)

redis_encoding_ziplist(压缩列表)

redis_encoding_intset(整数集合)

redis_encoding_skiplist(跳跃表和字典)

字符串的底层encoding编码结构是 int,raw 或者 embstr。

embstr 和 raw比较

好处:embstr 的使用只分配一次内存空间(因此 RedisObject 和 sds 是连续的),而 raw 需要分配两次内存空间(分别为 RedisObject 和 sds 分配空间)。因此与 raw 相比,embstr 的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。

坏处: 而embstr 的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个 RedisObject 和 sds 都需要重新分配空间,因此 Redis 中的 embstr 实现为只读。

如果一个字符串内容可转为 long,那么该字符串会被转化为 long 类型,对象 ptr 指向该 long,并且对象类型也用 int 类型表示。普通的字符串有两种 embstr 和 raw。如果字符串对象的长度小于 44字节,就用 embstr,否则用 raw。

为什么是44呢?

实际redis中存储数据的最小SDS结构是SDS_TYPE_8,结构:

可以看出,一个最小的SDS,至少占用3字节,再加上RedisObject的16个字节,也就是说一个最小的字符串是19个字节。

再了解下redis的内存分配器:jemalloc、tcmalloc。

这些内存分配器可以分配的大小2/4/8/16/32/64字节,可以看出最多分配64字节,即64K连续的内存。

64字节,减去RedisObject的16字节和SDS的3字节头信息,剩下45字节,再去除\0结尾,这样得出embstr可存储最大长度为64-16-3-1=44字节的字符串。 

底层数据编码格式转换

  • 当 int 数据不再是整数,或大小超过了 long 的范围时,自动转化为 raw。
  • 对于 embstr,由于其实现是只读的,因此在对 embstr 对象进行修改时,都会先转化为 raw 再进行修改。因此,只要是修改 embstr 对象,修改后的对象一定是 raw 的,无论是否达到了 44个字节。

ps:字符串追加空间扩展流程

 String的使用场景

number底层也是使用的String来实现的,所以可以用来实现自增、创建全局唯一的Id 、计数功能,另外还有分布式锁、位运算setbit

1)亿级用户登录

setbit date1225 101 1 //id=101的用户在1225日期登录

setbit date1225 90 1 //id=90的用户在1225登录过

setbit date1223 12 1 //id =12 在1223登录

bitcount date1225  //result :2    1225登录过2人

bitcount date1225 0 100   //result:1 前100个用户在1225登录了一人

strlen date1225 //  key:date1225保存的内容value大小是最大值101位除以8 = 12byte。所以bitmap的适用场景是数据量很大并且连续,否则value值占内存很大却保存的数据很少。

2)统计周活用户数

setbit date1225 10 1

setbit date1225 9 1

setbit date1225 7 1

setbit 1225 2 1

所以 1225日存储的数据是0 1 0 0 0 0 1 0 1 1

假如 1226登录的用户数据1 0 0 1 1 0 0 1 0 1

        1227登录的用户数据0 1 0 0 1 1 0 1 1 0

那么1225~1227的活跃用户有:

第一步:bitopt or user-num-1225~1227  date1225 date1226 date1227 ,三个日期每个用户登录标记取或运算。

第二步:bitcount user-num-1225~1227 = 9 


http://chatgpt.dhexx.cn/article/0XpZlsgY.shtml

相关文章

Redis-常用数据结构

Redis常用数据结构 Redis提供了一些数据结构供我们往Redis中存取数据&#xff0c;最常用的的有5种&#xff0c;字符串&#xff08;String&#xff09;、哈希(Hash)、列表&#xff08;list&#xff09;、集合&#xff08;set&#xff09;、有序集合&#xff08;ZSET&#xff09…

「Redis数据结构」集合对象(Set)

「Redis数据结构」集合对象&#xff08;Set&#xff09; 文章目录 「Redis数据结构」集合对象&#xff08;Set&#xff09;一、概述二、结构三、编码转换四、小结 一、概述 Set是Redis中的单列集合&#xff0c;其特点为不保证有序性、保证元素唯一、可以求交集、并集、差集。 …

图解redis五种数据结构底层实现

redis有五种基本数据结构&#xff1a;字符串、hash、set、zset、list。但是你知道构成这五种结构的底层数据结构是怎样的吗&#xff1f;今天我们来花费五分钟的时间了解一下。(目前redis版本为3.0.6) 动态字符串SDS SDS是"simple dynamic string"的缩写。redis中所…

redis底层数据结构-List

举例分析 创建列表对象 numbers 列表对象有两种底层实现结构 1.压缩列表(zipList)实现的列表对象 压缩列表(zipList)是Redis为了节省内存而开发的&#xff0c;是由一系列特殊编码的连续内存块组成的顺序型数据结构&#xff0c;一个压缩列表可以包含任意多个节点&#xff08;e…

redis中hash数据结构

目录 hash的数据结构ziplist底层实现字典底层实现扩容缩容 引用 hash的数据结构 hash底层数据结构的实现包括两种&#xff1a;ziplist和字典当保存的所有键值对字符串长度小于 64 字节并且键值对数量小于 512 时使用ziplist &#xff0c;否则使用字典的方式 ziplist底层实现 …

redis的五种数据结构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、字符串&#xff08;String&#xff09; 1.SDS的定义 2.SDS与C语言中字符串的区别&#xff08;优点&#xff09; 2.1 获取字符串长度 2.2 防止缓冲区的溢…

「Redis数据结构」哈希对象(Hash)

「Redis数据结构」哈希对象&#xff08;Hash&#xff09; 文章目录 「Redis数据结构」哈希对象&#xff08;Hash&#xff09;一、概述二、编码ZipListHashTable 三、编码转换 一、概述 Redis中hash对象是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储…

Redis底层数据结构(图文详解)

目录 前言 Redis为什么要使用2个对象&#xff1f;两个对象的好处 redisObject对象解析 String 类型 1、int 整数值实现 2、embstr 3、raw List 类型 1、压缩链表&#xff1a;ziplist 2、双向链表&#xff1a;linkedlist 3、快速列表&#xff1a;quicklist Hash …

Redis数据结构之hash

对象类数据的存储如果具有较频繁的更新需求操作会显得笨重&#xff0c;这里我们可以用redis的hash数据类型解决。 一、hash类型 新的存储需求&#xff1a;对一系列存储的数据进行编组&#xff0c;方便管理&#xff0c;典型应用存储对象信息 需要的存储结构&#xff1a;一个存储…

Redis数据结构之Zset

文章目录 一、zset数据结构二、跳表skipList什么是跳表&#xff1f;1.跳表的查找2.跳表的插入3.跳表的删除4.跳表的更新 一、zset数据结构 相比于set&#xff0c;sorted set 增加了一个权重参数 score&#xff0c;使得集合中的元素能够按 score 进行有序排列&#xff0c;还可以…

Redis数据结构有哪些

Redis数据结构有哪些 一、Redis数据结构 一、Redis数据结构 Redis是一种基于内存的数据库&#xff0c;并且提供一定的持久化功能&#xff0c;它是一种键值&#xff08;key-value&#xff09;数据库&#xff0c;使用 key 作为 索引找到当前缓存的数据&#xff0c;并且返回给程序…

「Redis数据结构」压缩列表(ZipList)

「Redis数据结构」压缩列表&#xff08;ZipList&#xff09; 文章目录 「Redis数据结构」压缩列表&#xff08;ZipList&#xff09;一、概述二、结构三、连锁更新问题四、压缩列表的缺陷五、小结参考 ZipList 是一种特殊的“双端链表” &#xff0c;由一系列特殊编码的连续内存…

redis数据结构hash

Redis数据结构之hash Hash存储结构 Hash是一个string 类型的field和value的映射表。Hash特别适合存储对象&#xff0c;相对于将对象的每个字段存成单个string 类型。一个对象存储在Hash类型中会占用更少的内存&#xff0c;并且可以更方便的存取整个对象。 我们简单举个实例来…

简述redis数据结构

String&#xff1a;字符串 List&#xff1a;列表 Hash&#xff1a;哈希表 Set&#xff1a;无序集合 Sorted Set&#xff1a;有序集合 bitmap&#xff1a;布隆过滤器 GeoHash&#xff1a;坐标&#xff0c;借助Sorted Set实现&#xff0c;通过zset的score进行排序就可以得到坐标附…

Redis数据结构之——hash

写在前面 以下内容是基于Redis 6.2.6 版本整理总结 一、Redis 数据结构hash的编码格式 Redis中hash数据类型使用了两种编码格式:ziplist(压缩列表)、hashtable(哈希表) 在redis.conf配置文件中&#xff0c;有以下两个参数&#xff0c;意思为&#xff1a;当节点数量小于512并…

Redis底层数据结构详解

Redis底层数据结构详解 我们知道Redis常用的数据结构有五种&#xff0c;String、List、Hash、Set、ZSet&#xff0c;其他的集中数据结构基本上也是用这五种实现的&#xff0c;那么&#xff0c;这五种是Redis提供给你的数据结构&#xff0c;这五种数据结构式怎么实现的&#xf…

Redis的五种基础数据结构

Redis的五种基础数据结构 Redis有5种基础数据结构&#xff0c;分别为:String(字符串)&#xff0c;list(列表)&#xff0c;hash(字典)&#xff0c;set(集合)和zset(有序集合)。 1.String(字符串) 字符串的结构 字符串String是Redis最简单的数据结构&#xff0c;它的内部表示…

Redis的数据结构

一、redis的数据结构 1、String字符串类型 Redis的String能够表示字符串、整数、浮点数三种值的类型 应用场景&#xff1a; 普通的赋值使用incr、decr命令进行递增和递减统计数据。用于实现乐观锁watch(事物)setNx实现分布式锁 底层数据类型&#xff1a; // 数据结构 stru…

为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

大家好&#xff0c;我是小林。 Redis 为什么那么快&#xff1f; 除了它是内存数据库&#xff0c;使得所有的操作都在内存上进行之外&#xff0c;还有一个重要因素&#xff0c;它实现的数据结构&#xff0c;使得我们对数据进行增删查改操作时&#xff0c;Redis 能高效的处理。…

IDEA 配置Services面板

IDEA中的services窗口是一个管理所有服务的地方&#xff0c;特别是在使用spring cloud项目开发中&#xff0c;对多个微服务的管理非常实用。 配置方式一&#xff1a; 在IDEA中可通过配置将services窗口面板显示出来。 配置方式二&#xff1a; 找到项目.idea目录下worksp…