redis源码解析

Warning

本文档主要是redis源码的解析, 在阅读本文档前最好先阅读 redis的设计与实现 。 源码的阅读主要是学习作者的代码风格、 编程技巧、 以及系统原理。 本文档主要就是介绍上面相关的三点, 源码是基于redis3.0。

Note

源码阅读方法:

  1. 自底向上:从耦合关系最小的模块开始读,然后逐渐过度到关系紧密的模块。就好像写程序的测试一样,先从单元测试开始,然后才到功能测试。我在刚开始读 Redis 源码的时候,使用的就是这种方法:先从单独的数据结构模块开始,然后再过渡到高层的功能模块。
  2. 从功能入手:通过文件名(模块名)和函数名,快速定位到一个功能的具体实现,然后追踪整个实现的运作流程,从而了解该功能的实现方式。我在读阻塞列表、数据库这种流程和功能都比较复杂,和其他文件耦合也比较多的模块时,使用的就是这样的方法。
  3. 自顶向下:从程序的 main() 函数,或者某个特别大的调用者函数为入口,以深度优先或者广度优先的方式阅读它的源码。我在阅读 redis.c/serverCron() 、 redis.c/main() 和 ae.c/aeMain() 这种有明显调用者性质的函数时,使用的就是这样的方法。

第一部分 内部数据结构

作者为了系统的性能, 实现了很多数据结构, 如sds、adlist、dict和skiplist。

第二部分 内存压缩结构

redis 还使用了一些特殊的存储结构, 在条件容许的情况下, 会使用压缩数据结构替代内部数据结构。 创建它们所消耗的内存通常比作用类似的内部数据结构要少得多, 如果使用得当, 压缩数据结构可以为用户节省大量的内存。 压缩数据结构的编码和操作方式要比内部数据结构要复杂得多, 所以所占用的 CPU 时间会比作用类似的内部数据结构要多。

第三部分 内存存储结构

Redis 是支持多key-value数据库(表)的,并用 RedisDb 来表示一个key-value数据库(表). redisServer 中有一个 redisDb *db成员变量, RedisServer 在初始化时,会根据配置文件的 db 数量来创建一个 redisDb 数组. 客户端在连接后,通过 SELECT 指令来选择一个 reidsDb,如果不指定,则缺省是redisDb数组的第1个(即下标是 0 ) redisDb. 一个客户端在选择 redisDb 后,其后续操作都是在此 redisDb 上进行的. 下面会详细介绍一下 redisDb 的内存结构.

digraph structure{
 fontname = "Verdana";
 fontsize = 10;
 rankdir=LR;
  
 node [fontname = "Verdana", fontsize = 10, color="skyblue", shape="record"];
  
 edge [fontname = "Verdana", fontsize = 10, color="crimson", style="solid"];
 
 
 redisDb [label="<head>redisDb|dict *dict|dict *expires|dict *blocking_keys|dict *ready_keys|dict *watched_keys|int id|long long avg_ttl"];
 dict [label="<head>dict|dictType *type|void *private|<dictht>dictht ht[2]|int rehashindex|int iterators"];
 redisServer [label="redisServer|<dict>redisDb *redisdb|list *clients|list *slaves,*monitors|<list>list *slowlog|dict *pubsub_channels|list *pubsub_patterns|..."];
 dictht [label="dictht|<head>dictEntry **table;|unsigned long size;|unsigned long sizemask;|unsigned long used;"]
 dictEntry [label="<head>dictEntry|<l1>void *key|<l2>|<l3>|<l4>|<l5>|"]
 string [label="<head>String|unsigned type:4;|unsigned notused:2;|unsigned encoding:4;|unsigned lru:22;|int refcount;|void *ptr;"]
 list [label="<head>list|unsigned type:4;|unsigned notused:2;|unsigned encoding:4;|unsigned lru:22;|int refcount;|void *ptr;"]
 hash [label="<head>hash|unsigned type:4;|unsigned notused:2;|unsigned encoding:4;|unsigned lru:22;|int refcount;|void *ptr;"]
 set [label="<head>set|unsigned type:4;|unsigned notused:2;|unsigned encoding:4;|unsigned lru:22;|int refcount;|void *ptr;"]
 zset[label="<head>zset|unsigned type:4;|unsigned notused:2;|unsigned encoding:4;|unsigned lru:22;|int refcount;|void *ptr;"]
 sdshr[label="REDIS_ENCODING_RAW(sdshr)"]
	 intst[label="REDIS_ENCODING_INT"]
 listst[label="REDIS_ENCODING_LINKEDLIST(list)"]
 ziplist[label="REDIS_ENCODING_ZIPLIST(ziplist)"]
 dictst[label="REDIS_ENCODING_HT(dict)"]
 intset[label="REDIS_ENCODING_INTSET(intset)"]
 skiplist[label="REDIS_ENCODING_SKIPLIST(skiplist)"]
 
 redisServer:dict -> redisDb:head;
 redisDb:head -> dict:head;
 dict:head -> dictht:head;
 dictht:head ->dictEntry:head;
 dictEntry:l1 ->string:head;
 dictEntry:l2 ->list:head;
 dictEntry:l3 ->hash:head;
 dictEntry:l4 ->set:head;
 dictEntry:l5 ->zset:head;
 string -> sdshr;
 string -> intst;
 list -> listst;
 list -> ziplist;
 hash -> ziplist;
 hash -> dictst;
 set -> dictst;
 set -> intset;
 zset -> ziplist;
 zset -> skiplist;
 //st_table_entry:next -> st_table_entry:head [style="dashed", color="forestgreen"];
}

第四部分 初始化

主要介绍redis.h/redis.c/redis-cli.c的内容,主要介绍介绍一些初始化过程,详细的应该在服务器调优的时候才可以体会。