Redis数据库里面的每个键值对都是由对象组成的,其中
- 数据库键总是一个字符串对象
- 数据库的值可以是字符串对象、列表对象、哈希对象、集合对象、有序集合对象这五种对象中的其中一种(现在可不仅仅就这两种了)
字符串
Redis中并没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串的抽象类型,并将SDS(simple dynamic string)用作Redis的默认字符串表示
在Redis里面,C字符串只会作为字符串字面量用在一些无须对字符串的值进行修改的地方,除此之外须要使用SDS
SDS还被用作缓冲区:AOF模块中的AOF缓冲区以及客户端状态中的输入缓冲区
struct sdshdr
{
// 记录buf数组中已使用字节的数量
int len;
// 记录buf数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串
char buf[];
}
Redis中字符串仍然保持了在字符串结尾的位置放置一个’\0’的惯例,但是这个空字符并不包含在len中,对于用户来说是完全透明的,这样做的好处是Redis可以直接使用C字符串函数库里面的函数
SDS和C字符串的区别
C字符串 | SDS |
---|---|
获取字符串长度的时间复杂度为O(N) | 获取字符串的事件复杂度为O(1) |
API是不安全的,可能会造成缓冲区溢出 | API是安全的,不会造成缓冲区溢出 |
维护字符串长度N次必然需要执行N次内存分配 | 修改字符串长度N次最多需要执行N次内存重分配 |
只能保存文本数据 | 可以保存文本或二进制数据 |
可以使用<string.h>中所有函数 | 可以使用<string.h>部分函数 |
SDS的空间预分配和惰性空间释放优化
空间预分配用于优化SDS的字符串增长操作:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS 分配修改所必须要的空间,还会为SDS分配额外的未使用空间。如果对SDS修改后,SDS的长度小于1MB,那个程序分配和len属性同样大小的未使用空间,否则将会分配1MB的未使用空间。这样做的好处是可以减少连续执行字符串增长操作所需的内存重分配次数。
惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多处来的字节,而是使用free属性将这样字节的数量记录起来,并等待将来使用。SDS避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化
链表
链表提供了高效的节点重排能力,以及顺序访问节点的能力,并且可以通过增删节点来灵活地调整链表的长度。链表在Redis中的应用非常广泛,比如列表键的底层实现之一就是链表。当一个列表键包含了数量比较多的元素或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现,除此之外发布与查询、慢查询、监视器等功能也用到了链表,Redis服务器本身还使用链表来保存多个客户端的状态信息以及构建客户端的输出缓冲区
Redis中数组的特点
- 双向无环链表
- 带表头指针和表尾指针
- 带链表长度计数器
- 多态(链表使用void*指针来保存节点值,并且可以通过相关的函数指针完成)
字典
字典,又称为符号表、关联数组,是一种用于保存键值对的抽象数据结构,键值是唯一的,字典在Redis中的使用非常广泛,比如Redis数据库就是使用字典作为底层实现的,对数据库的增删改出操作也是构建在字典的操作之上的。
字典还是哈希键的底层实现之一,当一个哈希键包含的键值对比较多或者键值对中的元素都是比较常的字符串时,Redis就会使用字典作为哈希键的底层实现
Redis中字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,每个哈希表节点就保存了字典中的一个键值对。