设为首页 收藏本站
查看: 1845|回复: 0

[经验分享] Redis源码解析(1)——源码目录介绍

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-7-19 13:30:28 | 显示全部楼层 |阅读模式
  概念
  redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
  
  开始
  这里我使用的是redis-2.2.2.tar.gz版本的redis(下载地址)首先要对它进行安装,这里我选择使用cygwin工具进行安装,加入我把该压缩包放在F盘下,使用cygwin工具进行shell命令:
  
   $ tar xzf redis-2.2.2.tar.gz
$ cd redis-2.2.2
$ make
  这里的make实际上操作的是Makefile文件,Makefile按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个shell脚本一样,其中也可以执行操作系统的命令。
  浏览下Redis根目录中的Makefile文件:



# Top level makefile, the real shit is at src/Makefile
TARGETS=32bit noopt test
all:
cd src && $(MAKE) $@
install: dummy
cd src && $(MAKE) $@
clean:
cd src && $(MAKE) $@
cd deps/hiredis && $(MAKE) $@
cd deps/linenoise && $(MAKE) $@
$(TARGETS):
cd src && $(MAKE) $@
src/help.h:
@./utils/generate-command-help.rb > $@
dummy:
  通过make命令可以执行“cd src && make all”。而此时的make all实际上已经开始执行src目录中的Makefile文件。这个文件比较复杂,大致就是将一系列的c文件以及h文件链接起来,通过cc/gcc编译器将文件生成目标文件o,接着将相应的o目标文件在通过编译器生成exe文件,当你编译完毕后,在src的目录上将产生5个exe文件:
DSC0000.png
  redis-benchmark.exe:用于做性能测试;
  redis-check-aof.exe:更新日志检查;
  redis-check-dump.exe:用于本地数据库检查;
  redis-cli.exe:客户端程序;
  redis-server.exe:服务端程序;
  具体用法这里不多说了,可以参考(http://www.iyunv.com/daizhj/articles/1956681.html)
  
  现在来看下src包含的文件(我按照首字母顺序来讲):
  adlist.h/adlist.c:用于对list的定义,它是个双向链表结构,从头文件可以找到:



// 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中的头部可以发现:



#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进行管理:



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可执行文件中。



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:用于事务处理操作。请看这样的一个例子:
DSC0001.png
  通过执行exec,可以提交整个事务过程,如果你想撤销整个事务过程,你可以使用discard命令:
DSC0002.png
  可以发现get age已经取不到值了,说明discard命令让事务失效。
  
  networking.c:网络协议传输方法定义相关的都放在这个文件里面了。包括让Client连接上Server,让Slave挂接到Master,已经Server/Client之间的信息交互的实现等等。
  
  object.c:用于创建和释放redisObject对象,redisObject结构为:



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方法以下设置:


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:用于对字符串的定义,从头文件可以找到:



//字符串
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的源码实现。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88311-1-1.html 上篇帖子: [译]Redis大冒险 下篇帖子: 关于 redis、memcache、mongoDB 的对比
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表