C中也有字符串这种基本结构,但是redis实现了自己的字符串,叫sds(simple dynamic string),sds是redis的默认字符串类型。
sds相比较c语言的原生字符串,有一些优点,有什么优点呢??这个得从redis定义的sds字符串结构说起:
// file : sds.h
struct sdshdr {
// 字符串当前长度,
//等于 buf 数组中已使用字节数
unsigned int len;
// 剩余可用长度
unsigned int free;
// 字符数组(具体存放字符串的地方)
char buf[];
};
sds的buf还是遵循以空字符结尾的惯例,这样可以直接重用c语言里面字符串函数库里的一些现成函数。
sds与c语言字符串的区别
常数复杂度获取字符串长度
因为 C 字符串并不记录自身的长度信息,所以获取一个 C 字符串的长度,必须遍历整个字符串,这个操作的的复杂度为 $O(N)$.
SDS 在 len 属性中记录了 SDS 的 len 属性,获取一个 SDS 长度的复杂度仅为$O(1)$
杜绝缓冲区溢出
减少字符串长度修改时带来的内存重新分配
对于 C 字符串,每次增长或 缩短一个 C 字符串,程序总要多保存这个 C 字符串的数组进行一次内存重分配操作:
- 如果执行的增长字符串的操作,在执行之前,程序要先通过内存重分配来扩展空间的大小,如果忘了可能会产生缓冲区溢出
- 如果执行的是缩短字符串的操作,在执行之后,程序要通过内存重分配来释放不再使用的部分空间,如果忘了,可能会产生内存泄露
内存重分配涉及到复杂的算法,并且可能需要执行系统调用.
- 在一般程序中,如果修改字符串长度的情况不太常出现,那么每次修改都执行一次内存重分配是可以接受的
- 但是 Redis 作为数据库,经常用于对于速度要求严苛,数据被频繁修改的场合,如果每次修改都执行一次内存重分配的话,光是执行内存重分配就会占去修改字符串所用时间的一大部分,若果修改频繁发生,可能会对系统性能造成影响
通过未使用空间sds中的feee能实现空间预分配和惰性空间释放这两种优化策略。
二进制安全
C字符串中的字符必须符合某种编码,除了末尾之外,其他位置不能包含空字符,否则第一个空字符会被误认为是结束符标识。这些限制使得c字符串只能保存文本数据,而不能保持图片/音频等这些一些二进制文件。
数据库保存二进制数据的场景并不少见,为了确保 Redis 可以使用各种不同的场景, SDS 中的 API 都是二进制安全的,所有的 SDS API 会以处理二进制的方式来处理 buf 中数据,程序不会对其中的数据做任何假设,数据在写入时什么,在读出时就是什么样。
网友评论