携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情
Redis为什么那么快?
除了redis所有的操作都在内存上之外,还因为实现的数据结构使得redis能让操作更加的高效。
redis数据结构并不是指String、list、hash这些。这些属于数据类型,是数据的保存形式,他们底层的实现方式用到了数据结构。
键值对数据库是怎么实现的?
redis中,key就是字符串对象,value可以是字符串对象也可以是集合数据类型的对象。
键值对是如何保存在redis中的呢?
redis使用哈希表保存所有键值对,因为可以用O(1)复杂度查找键值对,哈希表其实就是数组,数组中的元素叫做哈希桶。
redis的哈希桶是怎么保存键值对数据的呢?
哈希桶存放的是指向键值对数据的指针,键值对数据通过保存了 void * key 和 void * value 指针,指向实际的键对象和值对象,这样就保存集合数据也可以。
- redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;
- dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用,具体什么是 rehash,我在本文的哈希表数据结构会讲;
- ditctht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;
- dictEntry 结构,表示哈希表节点的结构,结构里存放了 **void * key 和 void * value 指针, key 指向的是 String 对象,而 value 则可以指向 String 对象,也可以指向集合类型的对象,比如 List 对象、Hash 对象、Set 对象和 Zset 对象。
void * key 和 void * value 指针指向的是 Redis 对象,Redis 中的每个对象都由 redisObject 结构表示。
- type 数据类型list string等等
- encoding 表示哪种数据结构
- ptr指向底层数据结构等指针
SDS
自己封装了简单动态字符串,没有使用C语言的char*字符数组来实现。
C语言字符串的缺陷
c语言字符串是一个字符数组,用“\0”结尾,表示字符串的结束。
C语言获取字符串长度的函数strlen,遍历每个字符直到遇到结束符号。复杂度为O(n)
如果中间有结束符号就会停止计数。
而且C语言字符串只能保存文本,不能保存图片音频
而且字符串的操作函数很容易溢出。
可以改进的地方:
- 获取长度复杂度O(N)
- 结束符号“\0”不能在中间,所以不能保存二进制数据
- 操作函数不高兴不安全
SDS结构设计
- len 记录字符串长度 复杂度O(1)
- alloc 分配给字符数组的空间长度,通过alloc -len 计算剩余大小,不足则自动扩容到修改所需大小。不需要手动修改,也没有缓冲区溢出问题
- flags 用来表示不同类型的SDS,5种,sdshdr5 8 16 32 64。区别在于len 和 alloc成员变量的数据类型不同,比如sdshdr32 则都是 uint32_t,表示表示字符数组长度和分配空间大小不能超过 2 的 32 次方。能够灵活保存节省内存空间。
- buf [] 字符数组,保存实际数据。也可以保存二进制数据
另外,除了设计不同类型的结构体,Redis 在编程上还使用了专门的编译优化来节省内存空间,即在 struct 声明了 attribute ((packed)) ,它的作用是:告诉编译器取消结构体在编译过程中的优化对齐,按照实际占用字节数进行对齐。
这是因为默认情况下,编译器是使用「字节对齐」的方式分配内存,虽然 char 类型只占一个字节,但是由于成员变量里有 int 类型,它占用了 4 个字节,所以在成员变量为 char 类型分配内存时,会分配 4 个字节,其中这多余的 3 个字节是为了字节对齐而分配的,相当于有 3 个字节被浪费掉了。
#include <stdio.h>
struct test1 {
char a;
int b;
} test1;
int main() {
printf("%lu\n", sizeof(test1));
return 0;
}
如果不想编译器使用字节对齐的方式进行分配内存,可以采用了 attribute ((packed)) 属性定义结构体,这样一来,结构体实际占用多少内存空间,编译器就分配多少空间。
#include <stdio.h>
struct __attribute__((packed)) test2 {
char a;
int b;
} test2;
int main() {
printf("%lu\n", sizeof(test2));
return 0;
}
\