Redis 源码学习笔记

Redis 源码学习笔记List 实现原理 字典实现原理 有序集合键实现 集合
前言
查看编码命令:key
判断对象类型:type key
五大数据类型实现原理实现原理
推荐书籍: Redis 设计与实现
推荐博客:#
字符串是Redis最基本的数据类型,不仅所有key都是字符串类型,其它几种数据类型构成的元素也是字符串 。注意字符串的长度不能超过512M 。
为什么字符串长度不能超过 512M?
// 源码定义(检查字符串长度)static int checkStringLength(redisClient *c, long long size) {if (size > 512*1024*1024) {addReplyError(c,"string exceeds maximum allowed size (512MB)");return REDIS_ERR;}return REDIS_OK;}
三种编码 int 编码:保存的是可以用 long 类型表示的整数值 。raw 编码:保存长度大于44字节的字符串(.2版本之前是39字节,之后是44字节) 编码:保存长度小于44字节的字符串(.2版本之前是39字节,之后是44字节)
127.0.0.1:6379> set str1 121OK127.0.0.1:6379> set str2 qweqweqweOK127.0.0.1:6379> set str3 qweqweqweqweqweqweqweqdhjajdskasjhdiqweuyaasddsaOK127.0.0.1:6379> object encoding str1"int"127.0.0.1:6379> object encoding str2"embstr"127.0.0.1:6379> object encoding str3"raw"
区别:
使用只分配一次内存空间(因此和sds是连续的),raw 需要分配两次内存空间(分别为和sds分配空间)
因此与raw相比,的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便 。而的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个和sds都需要重新分配空间,因此redis中的实现为只读 。
编码的转换
当 int 编码保存的值不再是整数,或大小超过了long的范围时,自动转化为raw 。
对于编码,由于 Redis 没有对其编写任何的修改程序( 是只读的),在对对象进行修改时,都会先转化为raw再进行修改,因此,只要是修改对象,修改后的对象一定是raw的,无论是否达到了44个字节 。
SDS 定义
struct sdshdr{// 记录 buf 数组中已使用字节的数量// 等于 SDS 所保存字符串的长度int len;// 记录 buf 数组中未使用字节的数量int free;// 字节数组,用于保存字符串char buf[];}
为什么要有 len ?
在 C 中使用 ‘\0’ 来标志一个字符串的结束,不能保存空字符否则会被认为是字符串的结尾 。如果有一个以空字符来分割单词的特殊特殊数据,则 C 只能读取第一个单词 。
而 Redis 用 buf[] 存储二进制数据,以 len 来判断结束,不会出现以上问题
为什么要有 free ?
空间预分配
在 C 中分配空间是一个比较耗时的工作,但 Redis 作为缓存数据库,数据的频繁修改是常态,而每次增长字符串都需要执行一次内存重分配(C 语言字符串处理特性),对性能产生很大影响 。
惰性空间释放
当字符串缩短时,程序并不立即使用内存重分配来回收多余的空间,而是用 free 记录下来,以便以后使用 。
SDS 提供了相应的 API 可以在我们需要的时候真正地释放 SDS 的未使用的空间 。所以不必担心惰性空间释放策略会浪费内存 。
为什么SDS 中 buf 数组仍然在一串字符串后面添加 ‘\0’ 标志 ?
SDS 中保存的文本数据可以根据这个标志调用 C 语言原有的函数(),如 (sds->buf, “hello world”)进行字符串对比,(, sds->buf)进行字符串追加等 。
List 实现原理 双向链表
// 链表的结点typedef struct listNode {// 前置节点struct listNode *prev;// 后置节点struct listNode *next;// 节点的值void *value;} listNode;
【Redis 源码学习笔记】