极客时间Redis源码学习笔记
Redis为什么快?
相信大家学Redis的时候,第一句话就是Redis是直接操作内存的,所以比操作磁盘IO的数据库要快。
但是只有这一点原因么?
答案肯定不是的,下面就是我在学习极客时间Redis源码时候的一些笔记和感想
Redis 是如何优化内存使用的呢?
实际上,Redis 是从三个方面来优化内存使用的,分别是内存分配、内存回收,以及数据替换。
首先,在内存分配方面,Redis 支持使用不同的内存分配器,包括 glibc 库提供的默认分配器 tcmalloc、第三方库提供的 jemalloc。Redis 把对内存分配器的封装实现在了 zmalloc.h/zmalloc.c。
其次,在内存回收上,Redis 支持设置过期 key,并针对过期 key 可以使用不同删除策略,这部分代码实现在 expire.c 文件中。同时,为了避免大量 key 删除回收内存,会对系统性能产生影响,Redis 在 lazyfree.c 中实现了异步删除的功能,所以这样,我们就可以使用后台 IO 线程来完成删除,以避免对 Redis 主线程的影响。
最后,针对数据替换,如果内存满了,Redis 还会按照一定规则清除不需要的数据,这也是 Redis 可以作为缓存使用的原因。Redis 实现的数据替换策略有很多种,包括 LRU、LFU 等经典算法。这部分的代码实现在了 evict.c 中。
内存分配?说的是数据结构么
C语言的字符串是涌char * 来实现的。但实际上,我们在使用 C 语言字符串时,经常需要手动检查和分配字符串空间,而这就会增加代码开发的工作量
Redis 设计了简单动态字符串(Simple Dynamic String,SDS)的结构,用来表示字符串。相比于 C 语言中的字符串实现,SDS 这种字符串的实现方式,会提升字符串的操作效率,并且可以用来保存二进制数据。
Redis字符串的实现方式
为什么Redis不用char *
要想解答这个问题,我们需要先知道 char* 字符串数组的结构特点,还有 Redis 对字符串的需求是什么,所以下面我们就来具体分析一下。
### C语言char* 的结构设计
char * 字符数组的结构很简单,就是一块连续的内存空间,依次存放了字符串中的每一个字符。比如,下图显示的就是字符串“redis”的char*数组结构。
从图中可以看到,字符数组的最后一个字符是“\0”,这个字符的作用是什么呢?其实,C 语言在对字符串进行操作时,char 指针只是指向字符数组的起始位置,而字符数组的结尾位置就用“\0”表示*,意思是指字符串的结束。
这样一来,C 语言标准库中字符串的操作函数,就会通过检查字符数组中是否有“\0”,来判断字符串是否结束。比如,strlen 函数就是一种字符串操作函数,它可以返回一个字符串的长度。这个函数会遍历字符数组中的每一个字符,并进行计数,直到检查的字符为“\0”。此时,strlen 函数会停止计数,返回已经统计到的字符个数。下图显示了 strlen 函数的执行流程:
再通过一段代码,来看下“\0”结束字符对字符串长度的影响。这里我创建了两个字符串变量 a 和 b,分别给它们赋值为“red\0is”和“redis\0”。然后,我用 strlen 函数计算这两个字符串长度,如下所示:
#include <stdio.h>
#include <string.h>
int main()
{
char *a = "red\0is";
char *b = "redis\0";
printf("%lu\n", strlen(a));
printf("%lu\n", strlen(b));
return 0;
}
结果:3,5
表示 a 和 b 的长度分别是 3 个字符和 5 个字符。这是因为 a 中在“red”这 3 个字符后,就有了结束字符“\0”,