Redis源码解析(1)——源码目录介绍

概念html

      redis是一个key-value存储系统。和Memcached相似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操做,并且这些操做都是原子性的。在此基础上,redis支持各类不一样方式的排序。与memcached同样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操做写入追加的记录文件,而且在此基础上实现了master-slave(主从)同步。redis

 

开始算法

      这里我使用的是redis-2.2.2.tar.gz版本的redis(下载地址)首先要对它进行安装,这里我选择使用cygwin工具进行安装,加入我把该压缩包放在F盘下,使用cygwin工具进行shell命令:shell

1
2
3
tar  xzf redis-2.2.2. tar .gz
cd  redis-2.2.2
make

     这里的make实际上操做的是Makefile文件,Makefile按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件须要先编译,哪些文件须要后编译,哪些文件须要从新编译,甚至于进行更复杂的功能操做,由于makefile就像一个shell脚本同样,其中也能够执行操做系统的命令。数据库

浏览下Redis根目录中的Makefile文件:缓存

      经过make命令能够执行“cd src && make all”。而此时的make all实际上已经开始执行src目录中的Makefile文件。这个文件比较复杂,大体就是将一系列的c文件以及h文件连接起来,经过cc/gcc编译器将文件生成目标文件o,接着将相应的o目标文件在经过编译器生成exe文件,当你编译完毕后,在src的目录上将产生5个exe文件:bash

image

redis-benchmark.exe:用于作性能测试;网络

redis-check-aof.exe:更新日志检查;数据结构

redis-check-dump.exe:用于本地数据库检查;app

redis-cli.exe:客户端程序;

redis-server.exe:服务端程序;

具体用法这里很少说了,能够参考(http://www.cnblogs.com/daizhj/articles/1956681.html

 

      如今来看下src包含的文件(我按照首字母顺序来说):

adlist.h/adlist.c:用于对list的定义,它是个双向链表结构,从头文件能够找到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// list节点
typedef  struct  listNode {
     struct  listNode *prev;
     struct  listNode *next;
     void  *value;
} listNode;
 
// list迭代器
typedef  struct  listIter {
     listNode *next;
     int  direction;
} listIter;
 
// list数据结构
typedef  struct  list {
     listNode *head;
     listNode *tail;
     void  *(*dup)( void  *ptr);
     void  (* free )( void  *ptr);
     int  (*match)( void  *ptr,  void  *key);
     unsigned  int  len;
} list;

在ListNode节点下包含prev指针和next指针,说明它经过指针将节点进行双向连接。

而且从adlist.h的头文件能够找到很是丰富的方法声明,包括list建立,list释放,list头部/尾部添加节点等等,具体在后面的系列会作出介绍。

 

ae.h/ae.c:用于Redis的事件处理,包括句柄事件和超时事件。

在ae.c中的头部能够发现:

1
2
3
4
5
6
7
8
9
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
     #ifdef HAVE_KQUEUE
     #include "ae_kqueue.c"
     #else
     #include "ae_select.c"
     #endif
#endif

      在网络相关操做中,定义了一组公共操做接口:aeApiCreate,aeApiFree,aeApiAddEvent,aeApiDelEvent,aeApiPoll,aeApiName方法。在ae_epoll.c、ae_kqueue.c和ae_select.c中,分别实现了基于epoll/kqueue和select系统调用的接口。系统调用的选择顺序依次为epoll,kqueue,select。

 

anet.h/anet.c:这两个文件很是重要,做为Server/Client通讯的基础封装,包括anetTcpServer,anetTcpConnect,anetTcpAccept,anetRead,anetWrite等等方法。

 

aof.c:aof,全称为append only file,做用就是记录每次的写操做,在遇到断电等问题时能够用它来恢复数据库状态。可是他不是bin的,而是text的。一行一行,写得很规范.若是你是一台redis,那你也能人肉经过它恢复数据。

 

config.h/config.c:用于将配置文件redis.conf文件中的配置读取出来的属性经过程序放到server对象中。在main函数(server服务主入口点处)能够发现里面调用loadServerConfig(char *filename)方法,这个方法就是使用config.c里面的方法实现。具体会在后面的系列中详细介绍。

 

db.c:对于Redis内存数据库的相关操做。

 

debug.c:用于调试使用。

 

dict.h/dict.c:也是很重要的两个文件,主要对于内存中的hash进行管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef  struct  dictEntry {
     void  *key;
     void  *val;
     struct  dictEntry *next;
} dictEntry;
 
typedef  struct  dictType {
     unsigned  int  (*hashFunction)( const  void  *key);
     void  *(*keyDup)( void  *privdata,  const  void  *key);
     void  *(*valDup)( void  *privdata,  const  void  *obj);
     int  (*keyCompare)( void  *privdata,  const  void  *key1,  const  void  *key2);
     void  (*keyDestructor)( void  *privdata,  void  *key);
     void  (*valDestructor)( void  *privdata,  void  *obj);
} dictType;
typedef  struct  dict {
     dictType *type;
     void  *privdata;
     dictht ht[2];
     int  rehashidx;  /* rehashing not in progress if rehashidx == -1 */
     int  iterators;  /* number of iterators currently running */
} dict;

这里dictEntry做为一个dict字段结构,里面包括key以及value,已经指向下一个dictEntry的指针。dictType做为一些dict的操做结构。dict做为一个hash结构。后面的文章会具体介绍。

 

fmacros.h:用于Mac下的兼容性处理。

 

help.h:辅助于命令的提示信息,做用于redis-cli.exe可执行文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct  commandHelp {
   char  *name;
   char  *params;
   char  *summary;
   int  group;
   char  *since;
} commandHelp[] = {
     "APPEND" ,
     "key value" ,
     "Append a value to a key" ,
     1,
     "1.3.3"  },
     "AUTH" ,
     "password" ,
     "Authenticate to the server" ,
     8,
     "0.08"  },
     "BGREWRITEAOF" ,
     "-" ,
     "Asynchronously rewrite the append-only file" ,
     9,
     ....
};

 

intset.h/intset.c:整数范围内的使用set,并包含相关set操做。

 

lzf.h/lzf_c.c/lzf_d.c/lzfP.h:对于本地数据库的保存,使用的是LZF压缩算法,很神奇,算法只有200-300行的代码。

 

multi.c:用于事务处理操做。请看这样的一个例子:

image

经过执行exec,能够提交整个事务过程,若是你想撤销整个事务过程,你可使用discard命令:

image

能够发现get age已经取不到值了,说明discard命令让事务失效。

 

networking.c:网络协议传输方法定义相关的都放在这个文件里面了。包括让Client链接上Server,让Slave挂接到Master,已经Server/Client之间的信息交互的实现等等。

 

object.c:用于建立和释放redisObject对象,redisObject结构为:

1
2
3
4
5
6
7
8
9
10
11
12
typedef  struct  redisObject {
     unsigned type:4;
     unsigned storage:2;      /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
     unsigned encoding:4;
     unsigned lru:22;         /* lru time (relative to server.lruclock) */
     int  refcount;
     void  *ptr;
     /* VM fields are only allocated if VM is active, otherwise the
      * object allocation function will just allocate
      * sizeof(redisObjct) minus sizeof(redisObjectVM), so using
      * Redis without VM active will not have any overhead. */
} robj;

 

pqsort.h/pqsort.c/sort.c:关于排序算法,sort.c具体做为Redis场景下的排序实现。

 

pubsub.c:用于订阅模式的实现,有点相似于Client广播发送的方式。

 

rdb.c:对于Redis本地数据库的相关操做,默认文件是dump.rdb(经过配置文件得到),包括的操做包括保存,移除,查询等等。

 

redis-benchmark.c:用于redis性能测试的实现。请看main方法如下设置:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
config.debug = 0;
config.numclients = 50;
config.requests = 10000;
config.liveclients = 0;
config.el = aeCreateEventLoop();
aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
config.keepalive = 1;
config.donerequests = 0;
config.datasize = 3;
config.randomkeys = 0;
config.randomkeys_keyspacelen = 0;
config.quiet = 0;
config.loop = 0;
config.idlemode = 0;
config.latency = NULL;
config.clients = listCreate();
config.hostip =  "127.0.0.1" ;
config.hostport = 6379;
config.hostsocket = NULL;
 
parseOptions(argc,argv);
config.latency = zmalloc( sizeof ( long  long )*config.requests);

默认性能测试中的客户端数量为50个,并行发送的请求有10000条,你也能够经过redis-benchmark命令行参数进行设置。

 

redis-check-aof.c:用于更新日志检查的实现。

 

redis-check-dump.c:用于本地数据库检查的实现。

 

redis-cli.c:客户端程序的实现。具体会在后面的文章详细介绍。

 

redis.h/redis.c:服务端程序的实现。具体会在后面的文章详细介绍。

 

release.h/release.c:用于发布使用。

 

replication.c:用于主从数据库的复制操做的实现。


sds.h/sds.c:用于对字符串的定义,从头文件能够找到:

1
2
3
4
5
6
//字符串
struct  sdshdr {
     int  len;
     int  free ;
     char  buf[];
};

还能够看到对于字符串的相关操做,包括复制,链接,清零等等。

 

sha1.h/sha1.c:有关于sha算法的实现。

 

solarisfixes.h:Solaris系统的兼容性实现。

 

syncio.c:用于同步Socket和文件I/O操做。

 

t_hash.c/t_list.c/t_set.c/t_string.c/t_zset.c:hash,list,set,string,zset在Server/Client中的应答操做。主要经过redisObject进行类型转换。

 

testhelp.h:一个C风格的小型测试框架。

 

util.c:关于通用工具的方法实现。

 

version.h:Redis版本号定义。

 

vm.c:关于虚拟内存的管理实现。

 

zipmap.h/zipmap.c:zipmap是一个相似于hash的存储对象。在新建一个hash对象时开始是用zipmap(又称为small hash)来存储的。这个zipmap其实并非hash table可是zipmap相比正常的hash实现能够节省很多hash自己须要的一些元数据存储开销,若是field或者value的大小超出必定限制后,redis会在内部自动将zipmap替换成正常的hash实现。

 

ziplist.h/ziplist.c:ziplist是一个相似于list的存储对象。它的原理相似于zipmap。

 

zmalloc.h/zmalloc.c:关于Redis的内存分配的封装实现。

 

 

下一篇我会介绍下redis-server以及redis-cli的源码实现。

相关文章
相关标签/搜索