Redis常用的数据结构有六种,分别是SDS(简单动态字符串)、链表、字典、跳跃表、整数集合与压缩列表。通过使用这些数据结构,Redis构建了一个对象系统,这个系统包含五种对象:字符串对象、列表对象、哈希对象、集合对象和有序集合对象,每个对象都拥有多种不同的数据结构实现,来应对不同的应用场景。
一、简单动态字符串(SDS)
1. 用途
当Redis需要的不仅仅是一个字符串字面量,而是一个可被修改的字符串值时,就会使用SDS来表述字符串的值。
2. SDS的结构定义
struct sdshdr {
//记录buf数组中已使用的字节数量
//等于SDS所保存字符串的长度,不包含'\0'的空间
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串,最后一个字节会保留字符'\0',但不计入len属性中,所以可以重用一些C语言的函数
char buf[];
}
3.SDS示例
4. SDS与C字符串的区别
4.1 常数复杂度获取字符串长度
C字符串本身不记录自身长度,所以需要遍历,时间复杂度为O(n)
SDS直接获取len,复杂度O(1)
4.2 杜绝缓冲区溢出
C字符串不记录自身长度,可能会造成缓冲区溢出
SDS拼接字符串时会进行检查,如果空间不够,会扩充
4.3 减少修改字符串时带来的内存重分配次数
SDS借助free解除了字符串长度与底层数组长度之间的联系。并且争对未使用空间,实现了两种SDS优化策略:
- 空间预分配
当对一个SDS进行修改时,若len小于1MB,分配与len相同的free,若len大于等于1MB,分配1MB的free。从而减少连续执行字符串增长操作所需的内存重分配次数。 - 惰性空间释放
当需要缩短字符串时,不立刻回收空间,而是作为free存在。当必须要释放时,可以使用相应的API进行释放。
4.4 二进制安全
C字符串必须符合某种编码,并且除了字符串的末尾之外,字符串中不能包含空字符,所以C字符串只能保存文本,而不能保存图片、音频等二进制数据。
SDS API会以处理二进制的方式来处理SDS存放在buf数组中的数据,不是以\0来界定结尾,而是通过len与buf来界定,所以SDS可以存储任意形式的数据。
4.5 <string.h>中的函数
c字符串可以使用所有<string.h>中的函数,SDS可以使用一部分,如比较函数strcasecmp。
二、链表
1. 链表的结构定义
//链表节点
typedef struct listNode{
//前置节点
struct listNode * prev;
//后置节点
struct listNode * next;
//节点的值
void *value;
}listNode;
//链表
typedef struct list{
//表头节点
listNode * prev;
//表尾节点
listNode * tail