sds

2024-04-28 01:08
文章标签 sds

本文主要是介绍sds,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

双向链表(adlist.h/adlist.c)

链表(list)是Redis中最基本的数据结构,由adlist.h和adlist.c定义。

数据结构

typedef struct listNode {//指向前一个节点struct listNode *prev;//指向后一个节点struct listNode *next;//值void *value;
} listNode;

listNode是最基本的结构,表示链表中的一个结点。结点有向前(next)和向后 (prev)两个指针,链表是双向链表。保存的值是void*类型。

typedef struct list {listNode *head;listNode *tail;void *(*dup)(void *ptr);void (*free)(void *ptr);int (*match)(void *ptr, void *key);unsigned long len;
} list;

链表通过list定义,提供头(head)、尾(tail)两个指针,分别指向头部的结点和尾部的结点;提供三个函数指针,供用户传入自定义函数,用于复制(dup)、释放(free)和匹配(match)链表中的结点的值(value);通过无符号长整数len,标示链表的长度。

typedef struct listIter {listNode *next;int direction;
} listIter;

listIter是访问链表的迭代器,指针(next)指向链表的某个结点,direction表示迭代访问的方向(宏AL_START_HEAD表示向前,AL_START_TAIL表示向后)。

digraph adlist {    rankdir=LR;    node [shape=record, style = filled, fillcolor = "#95BBE3"];    edge [style = bold];    list_node_1 [label = "<head>listNode |{<prev> prev| value|<next> next}", ];    list_node_2 [label = "<head>listNode |{<prev> prev| value|<next> next}"];    list_node_3 [label = "<head>listNode |{<prev> prev| value|<next> next}"];    list_node_1:next -> list_node_2:head;    list_node_2:next -> list_node_3:head;    list_node_2:prev -> list_node_1:head;    list_node_3:prev -> list_node_2:head;    node [width=1.5, style = filled, fillcolor = "#A8E270"];    list [label = "list |<head> head|<tail> tail|<dup> dup|<free> free|<match> match|<len> len"];    list:tail -> list_node_3:head;    list:head -> list_node_1:head;}

使用方法

Redis定义了一系列的宏,用于访问list及其内部结点。

链表创建时(listCreate),通过Redis自己实现的zmalloc()分配堆空间。链表释放(listRelease)或删除结点(listDelNode)时,如果定义了链表(list)的指针函数free,Redis会使用它释放链表的每一个结点的值(value),否则需要用户手动释放。结点的内存使用Redis自己实现的zfree()释放。

对于迭代器,通过方法listGetIterator()、listNext()、 listReleaseIterator()、listRewind()和listRewindTail()使用,例如对于链表list,要从头向尾遍历,可通过如下代码:

iter = listGetIterator(list, AL_START_HEAD); // 获取迭代器
while((node = listNext(iter)) != NULL)
{dosomething;
}
listReleaseIterator(iter);

listDup()用于复制链表,如果用户实现了dup函数,则会使用它复制链表结点的value。listSearchKey()通过循环的方式在O(N)的时间复杂度下查找值,若用户实现了match函数,则用它进行匹配,否则使用按引用匹配。

应用

除了实现列表类型以外, 双端链表还被很多 Redis 内部模块所应用:

  • 事务模块使用双端链表依序保存输入的命令;
  • 服务器模块使用双端链表来保存多个客户端;
  • 订阅/发送模块使用双端链表来保存订阅模式的多个客户端;
  • 事件模块使用双端链表来保存时间事件(time event);

字符串(sds.h/sds.c)

为了方便计算字符串的长度、以及提高字符串的拼接效率,作者实现了自己的字符串结构sdshdr,是二进制安全的,并在后面自动添加0。

数据结构

typedef char *sds;struct sdshdr {// 记录 buf 数组中已使用字节的数量// 等于 SDS 所保存字符串的长度int len;// 记录 buf 数组中未使用字节的数量int free;// 字节数组,用于保存字符串char buf[];
};

那么一个sds字符串实际申请的内存为:sizeof(sdshdr)+len+free+1,free新申请空间的时候为0,拼接字符串的时候free就不为0。

技巧

  1. 在函数sdsnewlen中,根据是否需要初始化使用zmalloc和zcalloc两个不同函数。
  2. 计算字符串长度的时候,直接使用函数sdslen,不需要调用strlen。

3. 需要扩展free的空间时,需要调用函数sdsMakeRoomFor,该函数空间分配策略比较有意思,如果free>=addlen,直接返回。否则判断free+addlen是否小于SDS_MAX_PREALLOC这个宏,如果小于,那么这次就分配2*(free+addlen)的空间,这样每次多分配一陪的空间;否则就分配free+addlen+SDS_MAX_PREALLOC的空间。这样可以控制最大多分配多少的空间,以至于不要浪费太多空间。例如:sds old=sdsnew("test one");sds new=sdscat(old,"test");此时有12的空余空间,如果再次调用``sdscat(new,”test”)``,那么就不需要分配空间。

4. 在函数sdscatvprintf中,空间申请是以16,32,64..这样增长的,无处不透露提高性能。

5. 在函数sdscmp中,调用memcmp,性能要比strcmp好,而且还是二进制安全的。

6. 在函数sdssplitlen中,默认分配的数组为5,然后按照2的倍数进行增长,这样做法,有点浪费空间,但是加快速度,不要每分割出来一个字符串就要申请空间。比较的时候把seplen为1分出来,也是加快字符串比较速度的考虑,大部分时候应该是seplen为1。

这篇关于sds的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/941976

相关文章

3D 生成重建011-LucidDreamer 优化SDS过平滑结果的一种探索

3D 生成重建011-LucidDreamer 优化SDS过平滑结果的一种探索 文章目录 0论文工作1论文方法2 效果 0论文工作 文本到3D生成的最新进展标志着生成模型的一个重要里程碑,为在各种现实场景中创建富有想象力的3D资产打开了新的可能性。虽然最近在文本到3D生成方面的进展显示出了希望,但它们在渲染详细和高质量的3D模型方面往往不足。这个问题特别普遍

【redis】字符串实现原理sds

redis 键值对中的key都是string类型的。redis内部实现中是怎么处理string呢?redis底层是用c写的,对于stirng并没有直接使用c的字符数组,而是自己封装了一个sds的类型。结构如下: buf数组用于存真正的字符。 为什么要新建数据类型?必然是为了抽象,是的编程更加简单。原有的c的字符串的api是不安全的,因为在使用字符数组以后,需要跟踪内存的分配。在使用之前,需要

Redis 的 SDS 和 C 中字符串相比有什么优势?

C 语言使用了一个长度为 N+1 的字符数组来表示长度为 N 的字符串,并且字符数组最后一个元素总是 \0,这种简单的字符串表示方式 不符合 Redis 对字符串在安全性、效率以及功能方面的要求。 C语言的字符串可能有什么问题? 这样简单的数据结构可能会造成以下一些问题: 获取字符串长度复杂度高 :因为 C 不保存数组的长度,每次都需要遍历一遍整个数组,时间复杂度为O(n);不能杜绝 缓冲区溢

以更多架构核心专利,推进 SDS 产业创新创造

今天是第 24 个世界知识产权日,今年世界知识产权日活动的主题是:“知识产权和可持续发展目标:立足创新创造,构建共同未来。” 这也正是 XSKY 在软件定义存储领域的目标之一。以“数据常青”为使命的 XSKY,始终立足于软件定义存储行业,坚持“创新架构”深入研发,引领行业的发展。 全新专利&nbsp;提升数据处理效率 就在近日,XSKY 刚刚获得了一款在星海极速全共享架构(XSEA)领域

Redis入门到通关之数据结构解析-动态字符串SDS

文章目录 Redis数据结构-动态字符串动态扩容举例二进制安全SDS优点与C语言中的字符串的区别 欢迎来到 请回答1024 的博客 🍓🍓🍓欢迎来到 请回答1024的博客 关于博主: 我是 请回答1024,一个追求数学与计算的边界、时间与空间的平衡,0与1的延伸的后端开发者。 博客特色: 在我的博客中,开设了如下专栏(点击可以进入专栏奥~): Java、M

Redis的String类型为什么重新设计使用了SDS数据结构呢

Redis 选择重新设计其 String 类型的底层数据结构,采用 SDS(Simple Dynamic String)而不是直接使用 C 语言标准库提供的原生字符串(char*)的原因主要包括以下几点: O(1) 时间复杂度获取长度: 在 C 语言中,获取一个以空字符 \0 结尾的字符串长度需要遍历整个字符串直到找到结束符,时间复杂度为 O(N)。SDS 在结构体头部显式记录了字符串的长度

Redis-SDS

文章目录 SDSSDS结构SDS优点效率高数据溢出内存重分配1.空间预分配2.惰性空间释放 数据格式多样性 SDS SDS(simple dynamic string),简单动态字符串,Redis String类型数据结构的底层实现。 Redis是用C语言开发的,但是Redis对于那些需要动态修改的字符串在其底层就会使用SDS,如:Redis中的key-value键值对含有

[redis 源码走读] sds

数据结构 为了节省空间,增加内存的利用率,struct 数据结构没有进行内存对齐,redis 的瓶颈不在 cpu 而在内存。同时,为了灵活处理不同长度范围的字符串,redis 定义了下面几种数据结构。 typedef char *sds;#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))#d

Redis字符串操作及SDS字符串实现

本专栏新增整合黄健宏老师的《Redis设计与实现》,以便更加有效地了解Redis 的内部构造及运作机制,帮助学者更高效得使用Redis。博文中会以虚线分区,上半区为Redis的简单使用,下半区为Redis内部实现,如果是简单地使用Redis快速完成工作或者只是想简单的了解Redis,只看上半区即可,有兴趣或等空闲的时候学习下下半区,结合源码会对Redis的理解更上一层楼。 redis-cli -

Redis核心数据结构之SDS(二)

SDS与C字符串的区别 杜绝缓冲区溢出 除了获取字符串长度的复杂度高之外,C字符串不记录自身长度带来的另一个问题是 容易造成缓冲区溢出(buffer overflow).例如<string.h>/strcat函数可以将src字符串 中的内容拼接到dest字符串的末尾: char *strcat(char *dest,const char *src); 因为C字符串不记录自身的长度,所以s