Redis 没有直接使用 C 语言传统的字符串表示,而是自己构建了一种名为简单动态字符串( simple dynamic string ,SDS) 的抽象 类型,并将 SDS 用作 Redis 的默认字符串表示。

SDS结构

总共有五类SDS结构,根据设置的字符串串长度,选择对应的结构体。

结构体中,len表示字符串的长度,alloc 表示字符串最大的长度,flags 低三位保存sds的类型,buf 占位符。

/* Note: sdshdr5 is never used, we just access the flags byte directly.* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) hisdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};

SDS与传统C字符串区别

1.获取字符串长度

C字符串: C 字符串并不记录自身的长度信息,所 以为了获取一个 C 字符串的长度,程序必须遍历整个字符串,直到遇到代表字符串结尾的空字符为止,这个操作的 复杂度为O(n).

SDS:SDS在 len 属性中记录了本身的长度,所以获取一个SDS长度的复杂度仅为O(1)。

2.缓冲区溢出

C字符串: C 字符串不记录自身长度带来的另一个问题是容易造成缓冲区溢出。例如strcat将一个字符串拼接到另一个字符串末尾,但是没有给目标字符串分配足够的空间,从而导致冲区溢出。

SDS:SDS 的空间分配策略完全杜绝了发生缓冲区溢出的可能性:当 SDS API 需要对 SDS 进行修改时, API 会先检查 SDS 的空间是否满足修改所需的要求,如果不满足的话. API 会自动将 SDS 的空间扩展至执行修改所需的大小,然后才执行实际的修改操作。

3.SDS空间预分配和惰性空间释放

空间预分配用于优化 SDS 的字符串增长操作:当 SDS 的 API 对一个 SDS进行修改, 并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必须要的空间,还会为 SDS 分配额外的未使用空间。

如果对 SDS 进行修改之后,SOS 的长度(也即是 len 属性的值)将小于1M, 那么程序分配和 len 属性同样大小的未使用空间。比如,如果进行修改之后, SDS 的 len 将变成13字 节,那么程序也会分配13字节的未使用空间, SDS 的 buf 数组的实际长度将变成 13+13+1=27 字节(额外的一字节用于保存空字符)。

如果对 SDS 进行修改之后, SDS 的长度将大于等于 1M,那么程序会分配 1MB 的未使用空间。举个例子,如果进行修改之后, SOS 的 len 将变成 30M,那么程序会分 配 1M的未使用空间, SDS 的 buf 数组的实际长度将为 30 M+ 1M+1byte

惰性空间释放用于优化 SDS 的字符串缩短操作,当 SOS 的 API 需要缩短 SDS 保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是等待将来使用。

4.二进制安全

C字符串: 除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读人的空字符将被误认为是字符串结尾,这些限制使得 C 字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二 进制数据。

SDS:SDS 的 API 都是二进制安全的,所有 SOSAPI 都会以处理二进制的方式来处理 SDS存放在 buf 数组里的数据, 程序不会对其中的数据做任何限制、过滤、或者假设,数据在写入时是什么样的,它被读取时就是什么样。

5.兼容C字符串函数

虽然 SDS 的 API 都是二进制安全的,但它们一样遵循 C 字符串以空字符结尾的惯例: 这些 API 总会将 SDS 保存的数据的末尾设置为空字符,并且总会在为 buf 数组分配空间时 多分配一个字节来容纳这个空字符, 这样SDS可以重用一部分C中的函数。

源码部分

扩容代码

/* Enlarge the free space at the end of the hisds string so that the caller* is sure that after calling this function can overwrite up to addlen* bytes after the end of the string, plus one more byte for nul term.** Note: this does not change the *length* of the hisds string as returned* by hi_sdslen(), but only the free buffer space we have. */
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {void *sh, *newsh;size_t avail = hi_sdsavail(s);size_t len, newlen;char type, oldtype = s[-1] & HI_SDS_TYPE_MASK;int hdrlen;/* Return ASAP if there is enough space left. */if (avail >= addlen) return s;len = hi_sdslen(s);sh = (char*)s-hi_sdsHdrSize(oldtype);newlen = (len+addlen);if (newlen < HI_SDS_MAX_PREALLOC)newlen *= 2;elsenewlen += HI_SDS_MAX_PREALLOC;type = hi_sdsReqType(newlen);/* Don't use type 5: the user is appending to the string and type 5 is* not able to remember empty space, so hi_sdsMakeRoomFor() must be called* at every appending operation. */if (type == HI_SDS_TYPE_5) type = HI_SDS_TYPE_8;hdrlen = hi_sdsHdrSize(type);if (oldtype==type) {newsh = hi_s_realloc(sh, hdrlen+newlen+1);if (newsh == NULL) return NULL;s = (char*)newsh+hdrlen;} else {/* Since the header size changes, need to move the string forward,* and can't use realloc */newsh = hi_s_malloc(hdrlen+newlen+1);if (newsh == NULL) return NULL;memcpy((char*)newsh+hdrlen, s, len+1);hi_s_free(sh);s = (char*)newsh+hdrlen;s[-1] = type;hi_sdssetlen(s, len);}hi_sdssetalloc(s, newlen);return s;
}

缩减容量

static inline void hi_sdssetlen(hisds s, size_t newlen) {unsigned char flags = s[-1];switch(flags&HI_SDS_TYPE_MASK) {case HI_SDS_TYPE_5:{unsigned char *fp = ((unsigned char*)s)-1;*fp = (unsigned char)(HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS));}break;case HI_SDS_TYPE_8:HI_SDS_HDR(8,s)->len = (uint8_t)newlen;break;case HI_SDS_TYPE_16:HI_SDS_HDR(16,s)->len = (uint16_t)newlen;break;case HI_SDS_TYPE_32:HI_SDS_HDR(32,s)->len = (uint32_t)newlen;break;case HI_SDS_TYPE_64:HI_SDS_HDR(64,s)->len = (uint64_t)newlen;break;}
}
void hi_sdsclear(hisds s) {hi_sdssetlen(s, 0);s[0] = '\0';
}

SDS结构

hisds s
s 指向内容起始地址
s[-1] 可以获取到type地址
s-sizeof(hisdshdr) 可以获取到sds内存起始地址

Redis数据结构之SDS相关推荐

  1. Redis 数据结构 :SDS、链表、字典、跳表、整数集合、压缩列表

    文章目录 SDS 结构分析 内存策略 空间预分配 惰性空间释放 总结 链表 结构分析 总结 字典 结构分析 rehash 渐进式rehash 总结 跳表 结构分析 总结 整数集合 结构分析 升级 降级 ...

  2. Redis 对象的数据结构原理 - SDS、Inset、Dict、ZipList、QuickList、SkipList、RedisObject

    Redis 数据结构 1. SDS Redis 是用 C 语言写的,但是对于 Redis 的字符串,却不是 C 语言中的字符串(即以空字符'\0'结尾的字符数组),它是自己构建了一种名为 简单动态字符 ...

  3. Redis 数据结构-字典源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...

  4. 为了拿捏 Redis 数据结构,我画了 40 张图

    Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...

  5. 【带你重拾Redis】Redis数据结构及使用场景

    Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...

  6. redis 数据结构

    2019独角兽企业重金招聘Python工程师标准>>> redis对象 redis 数据结构 字符串对象  SDS(简单动态字符串) 列表对象  压缩列表(ziplist) 或 链表 ...

  7. Redis 数据结构与内存管理策略(上)

    Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  8. redis value多大会影响性能_选择合适Redis数据结构,减少80%的内存占用

    前言 redis作为目前最流行的nosql缓存数据库,凭借其优异的性能.丰富的数据结构已成为大部分场景下首选的缓存工具. 由于redis是一个纯内存的数据库,在存放大量数据时,内存的占用将会非常可观. ...

  9. long 转为string_面试必问 Redis数据结构底层原理String、List篇

    点击关注上方"Java大厂面试官",第一时间送达技术干货. 阅读文本大概需要 8 分钟. 前言 今天来整理学习下Redis有哪些常用数据结构,都是怎么使用的呢?首先看下全局存储结构 ...

  10. 你需要知道的那些 redis 数据结构(前篇)

    戳蓝字"CSDN云计算"关注我们哦! 作者 | 饿了么物流技术团队 来源 | CSDN 企业博客 redis 对于团队中的同学们来说是非常熟悉的存在了,我们常用它来做缓存.或是实现 ...

最新文章

  1. 学术分享丨面向机器人的学习算法简述
  2. 316. Remove Duplicate Letters 去除重复字母
  3. Python如何防止sql注入
  4. 跟着微信后台团队学习分布式一致性协议
  5. Nginx添加ngx_http_headers_module模块
  6. 搜索引擎提交注意事项
  7. 7-137 凯撒密码 (20 分)
  8. 单元测试的思考与实践
  9. 【PRML 学习笔记】第二章 - 概率分布 (Probability Distributions)
  10. 实验2 双绞线的制作
  11. 微信小程序云开发问题篇3_云数据库导入Excel数据(xls转为json导入之后报错:导入数据库失败, Error: Poll error, 导入数据任务(id:1431654)异常...)
  12. 游戏是怎么赚钱的 - 迫不及待
  13. vuejs中用require引入图片,出现ReferenceError: require is not defined
  14. 参考线--深入了解字体
  15. android tv box ---- 插入u盘直接播放指定文件夹中的视频
  16. tk教主:个人成长_网络成长时:浏览器的故事
  17. matlab surf 坐标设置,matlab中3D曲面函数surf的坐标问题
  18. LeetCode刷题(python版)——Topic30串联所有单词的子串
  19. 互联网产品的定义及分类
  20. 区块链安全100问 |​ 第五篇:黑客通过这些方法盗取数字资产,看看你是否中招?

热门文章

  1. 易语言鼠标移动到桌面计算机图标上,易语言桌面图标提示源码
  2. win10 internet 信息服务器,Win10无internet访问权限怎么解决?Win10无网络解决方法
  3. SpringSecurity--记住我
  4. 八爪鱼抓取html,网页图片采集和抓取方法详解 - 八爪鱼采集器
  5. sql 错误码 备用
  6. 财务信息化系统架构设计
  7. linux quota原理,[转载]linux下quota实现
  8. 把Ethernet(以太网)基本工作原理说清楚
  9. PR连接蓝牙后无声音
  10. C# Activator实例化类的一般步骤