sds

news/2025/3/19 10:46:52/

双向链表(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。


http://www.ppmy.cn/news/272017.html

相关文章

SDD目标检测算法总结

SDD目标检测算法总结 一&#xff0c;SDD简介二、设计理念&#xff08;1&#xff09;采用多尺度特征用于检测&#xff08;2&#xff09;采用卷积进行检测&#xff08;3&#xff09;设置先验框 三、网络结构结尾 ​ 在这几年地发展中目标检测领域取得了较大的发展&#xff0c;相比…

SDD与SDT的区别

SDD与SDT的区别 这里拷贝了哈工大的SDD与SDT定义 SDD与SDT的区别: SDD 是关于语言翻译的高层次规格说明隐蔽了许多具体实现细节&#xff0c;使用户不必显式地说明翻译发生的顺序 SDT 可以看作是对SDD的一种补充&#xff0c;是SDD的具体实施方案显式地指明了语义规则的计算顺序…

编译原理笔记-SDD

编译原理笔记-SDD SDD与SDT的定义与区别见SDD与SDT的区别 语法制导定义 语法制导定义(Syntax-Directed Definition, SOD) 是一个上下文无关文法和属性及规则的结合。属性和文法符号相关联&#xff0c;而规则和产生式相关联。例子如下 属性分为综合属性和继承属性. 综合属性…

【编译原理】6—语法制导翻译Syntax-Directed Translation(SDD、SDT详细介绍)

6 语法制导翻译Syntax-Directed Translation ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 项目链接&#x1f449;https://github.com/A-BigTree/college_assignment ⭐⭐⭐⭐⭐⭐ 文章目录 6 语法制导翻译Syntax-Directed Translation6.1 语法制导定义…

软件(结构)设计说明(SDD)

说明&#xff1a; 1.《软件(结构)设计说明》(SDD)描述了计算机软件配置项(CSCI的设计。它描述了CSCI级设计决策、CSCI体系结构设计(概要设计)和实现该软件所需的详细设计。SDD可用接口设计说明IDD和数据库(顶层)设计说明DBDD加以补充。 2.SDD连同相关的IDD和DBDD是实现该软件…

语法制导定义 SDD

语法制导定义SDD是对于 上下文无关语法CFG 的一个推广&#xff1a; 将每个产生式和一组语义规则相关联&#xff0c;用来计算该文法产生式中每个文法符号的属性值。将每个文法符号和一个语义属性集合相关联。 也就是说&#xff0c;SDD为CFG的每个文法符号设置了一个语义属性&am…

大模型LLM

大模型LLM的1000篇文章总结 本文收集和总结了有关大模型LLM的1000篇文章&#xff0c;由于篇幅有限只能总结近期的内容&#xff0c;想了解更多内容可以访问&#xff1a;http://www.ai2news.com/, 其分享了有关AI的论文、文章、图书。 query NLP重铸篇之LLM系列(Anthropic LLM) …

什么蓝牙耳机性价比高?盘点1000以内性价比高的蓝牙耳机

近些年真无线市场发展可谓风生水起&#xff0c;层出不穷的真无线耳机让人眼花缭乱&#xff0c;怎样挑选可就成为了一个问题。我自己进入数码圈内也有小五年了&#xff0c;对于蓝牙耳机哪个好用这个话题还是有些经验的&#xff0c;接下来的五款蓝牙耳机是我为大家精心挑选的&…