redis底层数据结构

这几天在看redis设计与实现一书,总结一下学过的知识,以备后用。

一、数据结构:redis的底层数据结构有很多种,简单动态字符串,压缩列表,双端链表等 

redis 从数据结构分为string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

其实redis的底层数据结构有多种,比如 简单动态字符串sds,双端链表,压缩列表等。

1.简单动态字符串sds

redis底层为c语言,并没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组,以下简称 C 字符串), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将 SDS 用作 Redis 的默认字符串表示。在 Redis 里面, C 字符串只会作为字符串字面量(string literal), 用在一些无须对字符串值进行修改的地方, 比如打印日志.当 Redis 需要的不仅仅是一个字符串字面量, 而是一个可以被修改的字符串值时, Redis 就会使用 SDS 来表示字符串值。

结构:

struct sdshdr {

    // 记录buf数组中已使用字节的数量

    //等于SDS所保存字符串的长度

    int len;

    // 记录buf数组中未使用字节的数量

    int free;

    // 字节数组,用于保存字符串

    char buf[];

};

而sds也沿用了c字符串的传统,在字符串末尾加一个空格,这样可以复用一些c字符串的函数。

sds与c字符串的区别:                                                                                                                                                                                                  1.c字符串是不记录本身的长度的,如果想要获取字符串的长度,需要遍历数据知道遇到空格字符为止。复杂度为o(n),而sds字符串在结构中就存 在len与free属性,可以直接通过len属性来获取字符串的长度。

2.缓冲区溢出问题 : C字符串不记录自身长度, 修改字符串时不会判断本身是否拥有足够的内存空间, 当内存空间不足时, 则会造成缓冲区的溢出.SDS对字符串进行修改时,先检查内存空间是否满足修改的需要, 若不满足, 则自动扩展SDS的内存空间. 所以使用SDS既不需要手动修改内存空间的大小, 也不会出现缓冲区溢出的情况。

3.空间预分配:第一次创建字符串对象时, SDS不会分配冗余空间, 即 len = 0,当SDS的API修改SDS时, 则会为其分配冗余空间,当修改后的SDS的 len 属性小于1MB时, 则为其分配和 len 同样大小的冗余空间, 即 free = len, 此时 buf [ ] 的实际长度 = len(实际长度) + free(冗余空间) + 1(空字符),当修改后的SDS的 len 属性大于等于1MB时, 则为其分配1MB的冗余空间.  buf [ ] 的实际长度 = len(实际长度) + free(1MB) + 1(空字符)。

4.惰性空间释放:SDS的API缩短SDS的字符串时, 不会立即使用内存分配回收缩短后多出来的字节, 而是记录在 free 属性中, 并等待将来使用.

5.二进制安全:C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。SDS的API都是二进制安全的.所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制、过滤、或者假设,数据在写入时是什么样的,它被读 取时就是什么样。这也是我们将SDS的buf属性称为字节数组的原因——Redis不是用这个数组来保存字符,而是用它来保存一系列二进制数据。

你可能感兴趣的:(redis底层数据结构)