Redis数据结构之SDS
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相关推荐
- Redis 数据结构 :SDS、链表、字典、跳表、整数集合、压缩列表
文章目录 SDS 结构分析 内存策略 空间预分配 惰性空间释放 总结 链表 结构分析 总结 字典 结构分析 rehash 渐进式rehash 总结 跳表 结构分析 总结 整数集合 结构分析 升级 降级 ...
- Redis 对象的数据结构原理 - SDS、Inset、Dict、ZipList、QuickList、SkipList、RedisObject
Redis 数据结构 1. SDS Redis 是用 C 语言写的,但是对于 Redis 的字符串,却不是 C 语言中的字符串(即以空字符'\0'结尾的字符数组),它是自己构建了一种名为 简单动态字符 ...
- Redis 数据结构-字典源码分析
2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...
- 为了拿捏 Redis 数据结构,我画了 40 张图
Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...
- 【带你重拾Redis】Redis数据结构及使用场景
Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...
- redis 数据结构
2019独角兽企业重金招聘Python工程师标准>>> redis对象 redis 数据结构 字符串对象 SDS(简单动态字符串) 列表对象 压缩列表(ziplist) 或 链表 ...
- Redis 数据结构与内存管理策略(上)
Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...
- redis value多大会影响性能_选择合适Redis数据结构,减少80%的内存占用
前言 redis作为目前最流行的nosql缓存数据库,凭借其优异的性能.丰富的数据结构已成为大部分场景下首选的缓存工具. 由于redis是一个纯内存的数据库,在存放大量数据时,内存的占用将会非常可观. ...
- long 转为string_面试必问 Redis数据结构底层原理String、List篇
点击关注上方"Java大厂面试官",第一时间送达技术干货. 阅读文本大概需要 8 分钟. 前言 今天来整理学习下Redis有哪些常用数据结构,都是怎么使用的呢?首先看下全局存储结构 ...
- 你需要知道的那些 redis 数据结构(前篇)
戳蓝字"CSDN云计算"关注我们哦! 作者 | 饿了么物流技术团队 来源 | CSDN 企业博客 redis 对于团队中的同学们来说是非常熟悉的存在了,我们常用它来做缓存.或是实现 ...
最新文章
- 学术分享丨面向机器人的学习算法简述
- 316. Remove Duplicate Letters 去除重复字母
- Python如何防止sql注入
- 跟着微信后台团队学习分布式一致性协议
- Nginx添加ngx_http_headers_module模块
- 搜索引擎提交注意事项
- 7-137 凯撒密码 (20 分)
- 单元测试的思考与实践
- 【PRML 学习笔记】第二章 - 概率分布 (Probability Distributions)
- 实验2 双绞线的制作
- 微信小程序云开发问题篇3_云数据库导入Excel数据(xls转为json导入之后报错:导入数据库失败, Error: Poll error, 导入数据任务(id:1431654)异常...)
- 游戏是怎么赚钱的 - 迫不及待
- vuejs中用require引入图片,出现ReferenceError: require is not defined
- 参考线--深入了解字体
- android tv box ---- 插入u盘直接播放指定文件夹中的视频
- tk教主:个人成长_网络成长时:浏览器的故事
- matlab surf 坐标设置,matlab中3D曲面函数surf的坐标问题
- LeetCode刷题(python版)——Topic30串联所有单词的子串
- 互联网产品的定义及分类
- 区块链安全100问 |​ 第五篇:黑客通过这些方法盗取数字资产,看看你是否中招?