Redis设计与实现读书笔记(第一章)
一:内部数据结构与对象
1.sds的定义:
Struct sdshdr{
//记录buf数组中已使用的字节的数量 保存字符串的长度
Int len;
//数组中未使用的字节的数量
Int free;
//字节数字,用于保存字符串
Char buf[];
}
SDS遵循C字符串以空字符结尾。
2.SDS与C字符串的区别
C语言的使用长度是N+1的字符数组来表示长度为N的字符串。
C字符串不能记录自身的长度,所以获取C字符串的长度需要编目整个字符串,这个操作的复杂度为O(N).
和 C 字符串不同, 因为 SDS 在 len 属性中记录了 SDS 本身的长度, 所以获取一个 SDS 长度的复杂度仅为 O(1) 。
举个例子, 对于图 2-5 所示的 SDS 来说, 程序只要访问 SDS 的 len 属性, 就可以立即知道 SDS 的长度为 5 字节。
除了获取字符串长度的复杂度高之外, C 字符串不记录自身长度带来的另一个问题是容易造成缓冲区溢出(buffer overflow)。
与 C 字符串不同, SDS 的空间分配策略完全杜绝了发生缓冲区溢出的可能性: 当 SDS API 需要对 SDS 进行修改时, API 会先检查 SDS 的空间是否满足修改所需的要求, 如果不满足的话, API 会自动将 SDS 的空间扩展至执行修改所需的大小, 然后才执行实际的修改操作, 所以使用 SDS 既不需要手动修改 SDS 的空间大小, 也不会出现前面所说的缓冲区溢出问题。
总结:
表 2-1 C 字符串和 SDS 之间的区别
SDS |
C字符串 |
获取字符串长度的复杂度为 。 |
获取字符串长度的复杂度为 。 |
API 是安全的,不会造成缓冲区溢出。 |
API 是不安全的,可能会造成缓冲区溢出。 |
修改字符串长度 N 次最多需要执行 N 次内存重分配。 |
修改字符串长度 N 次必然需要执行 N 次内存重分配。 |
可以保存文本或者二进制数据。 |
只能保存文本数据。 |
可以使用一部分 <string.h> 库中的函数。 |
可以使用所有 <string.h> 库中的函数。 |
3.SDS API
函数 |
作用 |
时间复杂度
|
sdsnew |
创建一个包含给定 C 字符串的 SDS 。 |
O(N) , N 为给定 C 字符串的长度。 |
sdsempty |
创建一个不包含任何内容的空 SDS 。 |
O(1) |
sdsfree |
释放给定的 SDS 。 |
O(1) |
sdslen |
返回 SDS 的已使用空间字节数。 |
这个值可以通过读取 SDS 的 len 属性来直接获得, 复杂度为 O(1)。 |
sdsavail |
返回 SDS 的未使用空间字节数。 |
这个值可以通过读取 SDS 的 free 属性来直接获得, 复杂度为 O(1)。 |
sdsdup |
创建一个给定 SDS 的副本(copy)。 |
O(N) , N 为给定 SDS 的长度。 |
sdsclear |
清空 SDS 保存的字符串内容。 |
因为惰性空间释放策略,复杂度为 。 |
sdscat |
将给定 C 字符串拼接到 SDS 字符串的末尾。 |
O(N), N 为被拼接 C 字符串的长度。 |
sdscatsds |
将给定 SDS 字符串拼接到另一个 SDS 字符串的末尾。 |
O(N) , N 为被拼接 SDS 字符串的长度。 |
sdscpy |
将给定的 C 字符串复制到 SDS 里面, 覆盖 SDS 原有的字符串。 |
O(N), N 为被复制 C 字符串的长度。 |
sdsgrowzero |
用空字符将 SDS 扩展至给定长度。 |
O(N) , N 为扩展新增的字节数。 |
sdsrange |
保留 SDS 给定区间内的数据, 不在区间内的数据会被覆盖或清除。 |
O(N), N 为被保留数据的字节数。 |
sdstrim |
接受一个 SDS 和一个 C 字符串作为参数, 从 SDS 左右两端分别移除所有在 C 字符串中出现过的字符。 |
O(M*N) , M 为 SDS 的长度, N 为给定 C 字符串的长度。 |
sdscmp |
对比两个 SDS 字符串是否相同。 |
O(N) , N 为两个 SDS 中较短的那个 SDS 的长度。 |
在线阅读地址:
http://redisbook.com/preview/sds/different_between_sds_and_c_string.html#id2