因为 C 字符串的长度和底层数组的长度之间存在紧密关联, 所以每次增长或者缩短一个 C 字符串, 程序都要对这个数组进行一次内存重分配操作:
- 如果程序执行的是增长字符串的操作, 比如拼接操作(append), 那么在执行这个操作之前, 程序需要先通过内存重分配来扩展底层数组的空间大小 —— 如果忘了这一步就会产生缓冲区溢出。
- 如果程序执行的是缩短字符串的操作, 比如截断操作(trim), 那么在执行这个操作之后, 程序需要通过内存重分配来释放字符串不再使用的那部分空间 —— 如果忘了这一步就会产生内存泄漏。
因为内存重分配涉及复杂的算法, 并且可能需要执行系统调用, 所以通常是个比较耗时的操作。Redis 作为数据库, 经常被用于速度要求严苛、数据被频繁修改的场合, 如果每次修改字符串的长度都需要执行一次内存重分配的话, 那么光是执行内存重分配的时间就会占去修改字符串所用时间的一大部分, 如果这种修改频繁地发生的话, 可能还会对性能造成影响。
为了避免 C 字符串的这种缺陷, SDS 通过未使用空间解除了字符串长度和底层数组长度之间的关联: 在 SDS 中, buf 数组的长度不一定就是字符数量加 1, 数组里面可以包含未使用的字节, 而这些字节的数量就由 SDS 的 free 属性记录。
通过未使用空间, SDS 实现了空间预分配和惰性空间释放两种优化策略。
1、空间预分配:
空间预分配用于优化 SDS 的字符串增长操作: 简单来说就是当字节数组空间不足需要进行扩展时,总是会预留一部分空闲空间。
其中, 额外分配的未使用空间数量由以下公式决定:
- 如果对 SDS 进行修改之后, SDS 的长度(也即是
len 属性的值)将小于 1 MB , 那么多分配和 len 属性同样大小的未使用空间, 这时 SDS len 属性的值将和 free 属性的值相同。 - 如果对 SDS 进行修改之后, SDS 的长度将大于等于
1 MB , 那么程序会多分配 1 MB 的未使用空间。
通过空间预分配策略, Redis 可以减少连续执行字符串增长操作所需的内存重分配次数。
2、惰性空间释放:
惰性空间释放用于优化 SDS 的字符串缩短操作: 简单来说就是当字符串缩短时,并不立即使用内存重分配来回收多出来的字节, 而是使用 free 属性将这些字节的数量记录起来, 并等待将来使用。
通过惰性空间释放策略, SDS 避免了缩短字符串时所需的内存重分配操作, 并为将来可能有的增长操作提供了优化。同时SDS也提供直接释放SDS里面未使用空间的API,让我们在需要的时候,真正的释放掉多余的空间, 所以不用担心惰性空间释放策略会造成内存浪费。
虽然 SDS 的 API 都是二进制安全的, 但它们一样遵循 C 字符串以空字符结尾的惯例: 这些 API 总会将 SDS 保存的数据的末尾设置为空字符, 并且总会在为 buf 数组分配空间时多分配一个字节来容纳这个空字符, 这是为了让那些保存文本数据的 SDS 可以重用一部分 <string.h> 库定义的函数。
通过遵循 C 字符串以空字符结尾的惯例, SDS 可以在有需要时重用 <string.h> 函数库, 从而避免了不必要的代码重复。
《Redis 设计与实现》——黄健宏
Redis 没有直接使用 C 语言传统的字符串表示(以空字符\0结尾的char类型字符数组,以下简称 C 字符串), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将 SDS 用作 Redis 的默认字符串表示。在 Redis 里面, C 字符串只会作为字符串字面量(string literal), 用在一些无须对字符串值进行修改的地方,比如打印日志。SDS 的定义每个 sds.h / sdshdr 结构表示一个 SDS 值,在 Redis
Redis简单动态字符串
Redis不直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(SDS)的抽象类型。而在Redis中,C字符串只会作为字符串字面量。
redis> SET msg "hello world"
那么 Redis 将在数据库中创建了一个新的键值对, 其中:
键值对的键是一个字符串对象, 对象的底层实现是一个保存着字符串 "msg" 的 SDS 。
键值对的值也是一个字符串对象, 对象的底层实现是一个保存着字符串 "hello world" 的 SDS
2、SDS和C字符串的区别
1)常数复杂度获取字符串长度:C语言获取一个字符串的长度需要遍历整个字符串时间复杂度为O(N),而SDS在属性len中记录了字符串长度,获取字符串长度的时间复杂度为O(1)。
2)杜绝缓冲区溢出:C字符串在执行拼接字符串时,如果长度不够...
Redis字符串简介
在Redis中,字符串的存储不是使用C语言传统的字符串表示,而是使用一种名为简单动态字符串(simple dynamic string)的数据结构表示。
例如: 这一条指令
redis>SET msg "hello"
键值对的键是一个字符串对象,底层实现是一个保存着“msg”的SDS
键值对的值是一个字符串对象,底层实现是一个保存着“hello”的SDS
SDS...
本文对应Redis源代码的 src/sds.c 和 src/sds.h
Redis为了更有效率地管理字符串的内存问题,自己构建了一种简单动态字符串(simple dynamic string,简称sds),并将sds作为Redis的默认字符串来使用。sds会根据针对不同的长度的数据采用不同的数据结构,以达到节省内存的目的。如下共五种,其中SDS_TYPE_5并不使用,平时只是直接访问其标志字节:
#define SDS_TYPE_5 0
#defin
source/redis/src/sds.h
简单动态字符串(SDS,simple dynamie String),Redis底层是C编写的,但是Redis并没有直接使用C语言的字符串类型,而是自己创建了一套新的字符串类型,下面为SDS源码定义:
每个 sds.h/sdshdr 结构表示一个 SDS 值:
struct sdshdr { // redis 3.0
int len; // 记录 buf 数组中已使用字节的数量,等于 SDS 所
1、
SDS简介:
redis没有使用C语言传统的
字符串表示(以空字符结尾的字符数组),而是自己构建了一种名为
简单动态字符串(
SDS)的抽象类型,并将
SDS用作
redis的默认
字符串表示。
除了用来保存
数据库中的
字符串值之外,
SDS还被用作缓冲区;AOF模块中的AOF
缓存区,以及客户端状态中的输入缓冲区,都是
SDS实现的。
2、
SDS定义:
struct
sdshdr {
简单动态字符串(simple dynamic string)sds 作为redis的默认字符串。而不是直接使用C语言传统的字符串。
struct sdshdr{
int len;//记录buf数组中已使用的字节数量,等于sds保存的字符的长度
int free;//记录buf中未使用的字节数量
char buf[];//保存字符串
与C语言中string字符串相比,sds字符串有以...
Redis 没有直接使用C 语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串( simple dynamic string, SDS )的抽象类型,并将SDS 用作Redis 的默认字符串表示。
在Redis 里面, C 字符串只会作为字符串字面量( s回ng literal )用在一些无须对字符串值进行修改的地方,比如打印日志。
获取
字符串多长更快了!
这个解释起来
简单,
SDS结构体里存了当前
字符串的长度,直接读就行,时间复杂度为O(1),而C
字符串没存长度,统计长度的时候要把
字符串从头到尾的遍历一遍,时间复杂度O(n)。
不会有缓冲区溢出!
这个也好解释,对于C
字符串进行字符
1、简单动态字符串
redis没有直接用C语言传统的字符串(以空字符结尾的字符数组)表示,而是自己构建了一种名为简单动态字符串(SDS)的抽象类型,并将SDS用作redis的默认字符串表示。
在redis里面,C字符串只会作为字符串字面量用在一些无需对字符串值修改的地方,比如打印日志:
redisLog(REDIS_WAINING,"Rdeis is now ready to exit,bye bye...")
当redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,redis就会用S