标签:SDS 深入浅出 redis len 链表 字符串 buf 节点
1 数据结构与对象
1.1 SDS(simple dynamic string)
redis使用SDS表示字符串、缓冲区(AOF缓冲区)以及客户端状态中的输入缓冲区。
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
- free属性的值为0,表示这个SDS没有分配任何未使用空间
- len属性的值为5,表示这个SDS保存了一个5字节长的字符串
- buf属性是一个char类型的数组,最后以’\0’结尾。空字符不计算在SDS的len中
1.2 SDS与C字符串的区别
C字符串不能满足Redis对字符串在安全性、效率以及功能方面的要求
1.2.1 常数复杂度获取字符串长度
C字符串不记录自身的长度信息,因此获取一个C字符串的长度,需要遍历整个字符串,时间复杂度为 O(n), 而SDS里有len字段保存了字符串本身的长度,时间复杂度为O(1),确保了获取字符串长度不会成为redis的性能瓶颈
1.2.2 杜绝缓冲区溢出
C字符串不记录自身自身长度,因此容易造成缓冲区溢出,如:
# 如果用户为dest分配的空间不够,就会产生溢出,可能会导致src的内容被覆盖
char *strcat(char *dest, const char *src);
当使用SDS的API对SDS进行修改时,API会首先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作
1.2.3 减小内存重分配次数
SDS为了避免每次修改都要进行一次内存分配,使用free字段(未使用空间)来解除字符串长度和底层数组长度之间的关联,实现了空间预分配和惰性空间释放两种优化策略。
- 空间预分配
- 如果对SDS进行修改后,SDS的长度(len的值)小于 1MB,那么程序将分配2倍的len,即len和free的值相等,buf的实际长度为2len + 1
- 否则(len>=1MB),程序将分配len+1MB,buf的实际长度为len+1MB+1
- 惰性空间删除
当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重新分配来回多出来的空间,而是使用free属性将这些字节的数量记录起来,并等待将来使用
1.2.4 二进制安全
C字符串里不能包含空字符串(’\0’),而SDS使用len来判断字符串是否结束,因此SDS的buf可以存放二进制数据
1.2.5 兼容部分C字符串函数
SDS的API虽然都是二进制安全的,但是一样遵循C字符串以空字符串结尾的惯例。这些API总是为SDS保存的数据的末尾设置空字符,并且总会在为buf数组分配空间时多分配一个字节来容纳这个空字符。
好处:
- redis不必自己专门写函数来对比SDS与C字符串了,可以直接使用string.h的函数库,如:
strcat(c_string, sds->buf);
1.2 链表
链表用于redis的链表键、发布与订阅、慢查询、监视器等等。
多个listnode可以通过prev和next指针组成双端链表。
/*
* 双端链表节点
*/
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode;
虽然仅仅使用多个listNode结果就可以组成链表,但使用adlist.h/list来持有链表的话,操作起来会更方便:
/*
* 双端链表结构
*/
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;
- dup函数用于复制链表节点所保存的值
- free函数用于释放链表节点所保存的值
- match函数则用于对比链表节点所保存的值和另一个输入值是否相对
redis的链表实现特效:
- 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都为O(1)
- 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点
- 带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)
- 代链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,获取节点数量的复杂度为O(1)
- 多态:链表节点使用void*指针来保存节点的值,且可以通过dup、free、match为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值
标签:SDS,深入浅出,redis,len,链表,字符串,buf,节点 来源: https://blog.csdn.net/kaydxh/article/details/106290977
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。