Redis学习——SDS字符串实现

0. 前言

  这里对Redis底层字符串的实现分析,但是看完其实现还没有完整的一个概念,即不太清楚作者为什么要这样子设计,只能窥知一点,需要看完redis如何使用再回头来体会,有不足之处还望告知。

  涉及文件:sds.h/sds.c

1.  数据结构:  

1 typedef char *sds;
2
3 struct sdshdr {
4     unsigned int len;    //buf中已使用的字节数
5     unsigned int free;    //buf中未使用的字节数
6     char buf[];        //缓冲区
7 };

  这里向外提供的api所返回的类型都是sds类型(字符串),这样的话也能够复用一部分的C字符串函数。

  这里采用sdshdr结构,存放了字符串长度信息,保证了二进制数据安全,即不仅可以存放字符串,也可用于存放其它二进制数据

2. API实现:

  只提取几个API,该文件完整的注释在GitHud上(用户名:jabnih)

a. sdsnewlen

  创建一个sds字符串,其它几个创建API都是基于这个API。

  创建时采用一次性分配其所需要的空间,即对于buf不进行再次分配,减少了malloc等的调用,同时在释放的时候也减少free次数

 1 //创建一个sds字符串,初始内容为init所指向的内容,buf空间为initlen大小
 2 sds sdsnewlen(const void *init, size_t initlen) {
 3     struct sdshdr *sh;
 4
 5     //这里需要注意
 6     if (init) {
 7         //init不为空,则使用malloc,所申请的空间不会初始化
 8         sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
 9     } else {
10         //init为空,使用calloc,所申请的空间会被初始化为0
11         sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
12     }
13
14     if (sh == NULL) return NULL;
15
16     sh->len = initlen;
17     sh->free = 0;
18     //这里如果init为NULL,则该buf的内容均为0
19     if (initlen && init)
20         memcpy(sh->buf, init, initlen);
21
22     sh->buf[initlen] = ‘\0‘;
23
24     return (char*)sh->buf;
25 }

b. sdsMakeRoomFor

  该API的内存分配策略为:在小于SDS_MAX_PREALLOC(即1M)时,会预分配出多一倍的空间,在大于该阈值时,每次只预分配多SDS_MAX_PREALLOC内存。

 1  //保证sds字符串有足够的剩余未使用空间(大于或等于addlen)
 2 sds sdsMakeRoomFor(sds s, size_t addlen) {
 3     struct sdshdr *sh, *newsh;
 4     size_t free = sdsavail(s);
 5     size_t len, newlen;
 6
 7     //其剩余的空间满足addlen大小
 8     if (free >= addlen) return s;
 9
10     //不满足addlen大小,需要重新分配
11     len = sdslen(s);
12     sh = (void*) (s-(sizeof(struct sdshdr)));
13     //新空间所需使用的大小为当前sds使用的长度加上addlen
14     newlen = (len+addlen);
15     //如果新空间大小比设定的阈值小,则以2倍的增长速度预分配一些空间
16     if (newlen < SDS_MAX_PREALLOC)
17         newlen *= 2;
18     else
19         //比设定阈值大,则只增加PREALLOC预分配大小
20         newlen += SDS_MAX_PREALLOC;
21     //重新分配空间
22     newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
23     if (newsh == NULL) return NULL;
24
25     newsh->free = newlen - len;
26     return newsh->buf;
27 }

c. sdsRemoveFreeSpace

 1  //去除sds字符串中未使用的空间,一般在内存紧张的时候使用
 2 sds sdsRemoveFreeSpace(sds s) {
 3     struct sdshdr *sh;
 4
 5     sh = (void*) (s-(sizeof(struct sdshdr)));
 6     sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
 7     sh->free = 0;
 8
 9     return sh->buf;
10 }

d. sdsclear

1  //清空sds字符串,但是不释放空间
2 void sdsclear(sds s) {
3     struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
4
5     sh->free += sh->len;
6     sh->len = 0;
7     sh->buf[0] = ‘\0‘;
8 }

3. 总结:

  1. 二进制数据安全

  2. 预分配空间,可以懒惰释放,在内存紧张的时候也可以缩减不需要的内存

  3. 使用该API可以实现内存动态扩展(即不需要考虑内存空间是否足够)

  4. 边界检查

时间: 2024-12-29 01:39:04

Redis学习——SDS字符串实现的相关文章

Redis学习笔记---字符串类型

补充 上篇笔记博客中有些有些关键点未提到,现在这里补充下 redis help 命令 help命令应该是我们使用任何一款软件产品经常用到的命令,有时候通过help命令能够更快的获取相关帮助,而不仅仅通过百度.同样,在Redis中的help命名写的就非常简洁易懂,虽然是只有英文版的帮助信息,但是只要有一定应用基础的人都可以看懂,如: 127.0.0.1:6379> help redis-cli 3.0.2 Type: "help @<group>" to get a l

redis学习-sds数据类型

今天开始了redis的学习,本来想直接从源码看起的,不过看到有篇对redis介绍的基础教程 <Redis 设计与实现(第一版)> 于是决定从这个开始入门. 1.数据类型定义 typedef char *sds; struct sdshdr { // buf 已占用长度 int len; // buf 剩余可用长度 int free; // 实际保存字符串数据的地方 char buf[]; }; 2.重新计算新的长度 newlen = sdshdr.len + required_len if n

Redis学习之字符串类型详解

本文和大家分享的主要是Redis中字符串相关内容,一起来看看吧,希望对大家学习redis有所帮助. 字符串类型是最基本的数据类型,能够存储所有的字符串,包括二进制数据 json 化对象, 一个字符串类型的键的数据的最大容量是 512 MB 字符串类型是其他几种类型的基础,如列表类型是以列表的形式组织字符串,集合类型是以集合的形式组织字符串 获得符合规则的键名列表 keys pattern # ? 匹配一个字符 # *  匹配任意一个字符 # [] 匹配括号中的任意一个字符.可以使用 - 表示范围

Redis学习笔记(字符串类型常见命令操作)

Redis字符串类型常见命令操作 存取数据 存字符串类型数据: 取字符串类型数据: 当键不在的时候会返回空 如果重复向一个键中存数据,后存储的数据会覆盖前存储的数据 递增数字 当存的字符串是整数形式的时候,Redis提供一个: INCR命令    作用:让当前键值递增,并返回递增后的值 当要递增的键值不存在的时候,redis会自动存入这个键,默认值为0,并把值递增 键的命名 键名Redis没有强制要求,一般命名形式为: 对象类型:对象ID:对象属性 例如: user:1:name  使用这个来存

小白的Redis学习(一)-SDS简单动态字符串

本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态字符串(以下简称SDS),该字符串的结构如下 struct sdshdr{ //记录buf数组中已使用字节的数量 //获取字符串的长度时,就是直接返回的这个字段的值 int len; //记录buf数组中未使用字节的数量 int free; //字节数组,用于保存字符串 char buf[]; } SDS遵循

Redis源码学习:字符串

Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串,还用来当做缓冲区,例如AOF缓冲区或输入缓冲区等.如下所示,整数len和free分别表示buf数组中已使用的长度和剩余可用的长度,buf是一个原生C字符串,以\0结尾. sds就是sdshdr中char buf[]的别名,后面能看到,各种操作函数的入参和返回值都是sds而非sdshdr.那sdshd

redis学习笔记(11)---字符串命令及实现

对象类型与编码方式 对于字符串类型的命令,redis数据库会为每个对象创建一个字符串类型(REDIS_STRING)的对象. 对于字符串类型的对象,可以支持三种编码方式: #define REDIS_ENCODING_RAW 0 /* Raw representation */ #define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string e

关于redis中SDS简单动态字符串

1.SDS 定义 在C语言中,字符串是以’\0’字符结尾(NULL结束符)的字符数组来存储的,通常表达为字符指针的形式(char *).它不允许字节0出现在字符串中间,因此,它不能用来存储任意的二进制数据. sds的类型定义 typedef char *sds; 肯定有人感到困惑了,竟然sds就等同于char *? sds和传统的C语言字符串保持类型兼容,因此它们的类型定义是一样的,都是char *,在有些情况下,需要传入一个C语言字符串的地方,也确实可以传入一个sds. 但是sds和char

Redis源码阅读-sds字符串源码阅读

redis使用sds代替char *字符串, 其定义如下: typedef char *sds; struct sdshdr { unsigned int len; unsigned int free; char buf[]; }; sds指向了char 字符串 sdshdr是字符串头 结构比较巧妙 使用char buf[]存放字符串实际内容 注意char *buf和char buf[]是不同的 sizeof(sdshdr)等于8,而不是我以为的12 连续内存结构如下: 0----7 sdshd