Redis源码阅读

鉴于Redis目前实在很火,而且传说核心代码是4万多行c,让人时不时有些想要一探究竟的小冲动。

找个好天气,行动吧!下载git,留几句阅读源码比较有用的git命令备用。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. git clone https://github.com/antirez/redis.git  
  2. git log > ../redis.log  
  3. git checkout -b <branch-name> <commit-id>  
  4. git branch  
  5. git checkout <branch-name>  

第1句话是下载redis源码,第2句是输出日志,第3句新建并切换分支,第4句列出全部分支,第5句切换分支。

有了这5句话,git部分基本就没啥问题了。

个人阅读源码的习惯是不管三七二十一先切换到第一个commit。刚才已经说了切换分支的命令了,git checkout -b first-commit ed9b544e10b84cd43348ddfab7068b610a5df1f7。<commit-id>的来源,打开redis.log,最抢眼的就是它了!然后,我还会做的一件事是统计一下源码有几行:)

cat *.c *.h | wc -l,一看这第一个commit的redis一共有6232行源码,看来代码不长,顿觉信心百倍。

从哪开看呢?我想README会是个好地方,结果它推荐查看doc/README.html,那就看吧。整理一下我看到的知识点:

  1.   In Redis keys are just strings too, but the associated values can be Strings, Lists and Sets。这里需要看一下value是String, List, Set在redis里面是如何实现的,虽然估计是直接用指针实现。
  2.   Redis writes a dump of the dataset on disk asynchronously. 异步回写磁盘也需要多加关注。
  3.   the idea is to provide atomic primitives in order to make the programmer able to use redis with locking free algorithms. OK,原子操作的实现也是Redis的杀手锏。
  4.   Another synchronization primitive is the support for multiple DBs.多DB的知识,也作为是Redis的一个特色,不过设计这个东西究竟是何意图,对于初学者来说还是值得推敲。
  5.  数据类型的支持,目前第一版本是Striing, List, Set。

下一个是谁呢?Makefile!来看看redis会编译出一些什么东西。看到all的依赖后面跟的是redis-server redis-benchmark redis-cli,就知道最后会编译出来的是这三个东西。然后看redis-server的依赖,稍微动一下小脑袋就会发现会有adlist.c ae.c anet.c dict.c redis.c sds.c zmalloc.c这几个源文件会成为待会儿的下酒菜。

在看redis.c这个98KB的大家伙之前,先在源码中四周游荡一下,看看client-libraries,里面藏了很多其他语言编写的代码,我就来看看python的吧,里面只有一个redis.py,打开文件它的描述是A client for the Redis daemon。代码结构比较简单,在几个异常类的定义之后,就只有一个Redis类,里面就藏了很多的redis命令了,每个都有使用案例的,还是非常容易看的。再瞄一眼redis-cli.c,有command定义,以及main函数,一扫而过,终归就是建立与服务器的通信,然后发命令,接受服务器的回复。晚些再来细读。

饶了好大一个弯子,终于到了redis.c,3037行代码,直接开看就是了。边看边摘抄:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* A redis object, that is a type able to hold a string / list / set */  
  2.  typedef struct redisObject {  
  3.      int type;  
  4.      void *ptr;  
  5.      int refcount;  
  6.  } robj;  
这一段代码就说明了 redis为什么可以支持string,list,set了,因为它们都是一个对象 ,有类型有地址还有引用计数,还给取了这么好的一个名字redisObject。
后续在1303行的地方还定义了一堆函数对这个object进行管理。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct _redisSortObject {  
  2.     robj *obj;  
  3.     union {  
  4.         double score;  
  5.         robj *cmpobj;  
  6.     } u;  
  7. } redisSortObject;  

这一段代码有一点继承的味道了。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. li = listGetIterator(server.clients,AL_START_HEAD);  
  2. if (!li) return;  
  3. while ((ln = listNextElement(li)) != NULL) {  
  4.     c = listNodeValue(ln);  
  5.     if (!(c->flags & REDIS_SLAVE) &&    /* no timeout for slaves */  
  6.          (now - c->lastinteraction > server.maxidletime)) {  
  7.         redisLog(REDIS_DEBUG,"Closing idle client");  
  8.         freeClient(c);  
  9.     }  
  10. }  
  11. listReleaseIterator(li);  
这段代码又传授给了我们如何使用c语言来做 迭代器 编码。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static void createSharedObjects(void) {  
  2.     shared.crlf = createObject(REDIS_STRING,sdsnew("\r\n"));  
  3.     shared.ok = createObject(REDIS_STRING,sdsnew("+OK\r\n"));  
  4.     ...  
  5.     shared.select9 = createStringObject("select 9\r\n",10);  
  6. }  
共享对象这一段也挺牛的,这个貌似就是 享元(FLYWEIGTH)模式 。究竟是不是叫这个名字,以及多大程度上节约了内存,以后再去推敲。

似乎与磁盘回写相关的代码在serverCron这个函数里面,为何做如此判断呢?印象中cron是一个linux计划安排的程序。

Redis号称的多server,貌似是有主从之分,看到replicationFeedSlaves,我已经基本把slave这个词映入脑海了。
最后的一个大问题就是,Redis如何保证原子操作的,我专门看了一下incrCommand但是没有看出来,具体是如果做到原子操作的。

看redis.c之后,那么下一个目标就是sds.c了,因为redis里面的string总号称自己是sds string,至于什么是sds string,我们就通过sds.c这个源码来一探究竟吧。看来antirez这哥们着实是重写了一个string操作的库函数,代码不长329行。或许除了提供特定的字符串操作函数之外,最大的特色就是使用zmalloc.c里面内存操作的包装函数来做了内存管理。给我的感觉是,redis为所有的对象管理建立了一套特有的管理方案,string作为一个对象也绝不例外。

到了zmalloc.c东西就比较简单了,82行代码,封装了初始的c语言内存分配的函数malloc,realloc,free,然后维护一个内存使用状况的变量used_memory。
最后配合上封装socket的anet.c,消息循环的ae.c;adlist.c实现链表操作,dict.c实现哈希表操作来为set提供基础之后,redis大体的内容就结束了。
现在想来redis之所以支持原子操作,难道是因为,一次一个服务器只和一个客户端进行连接,而且服务器是运行在单线程模式下才得以保证的?

后续切回到Redis的最新代码上去,git checkout unstable,发现核心源码已经放入到src中,虽然多了bio, aof, cluster等诸多内容,但是总的来说,框架性的东西大体变化都不大。

总结:总的来说,本次源码阅读的宗旨是想看一下redis究竟长啥样,看完之后,积累的知识主要包括的C语言的面向对象设计,以及基本数据结构和算法的实际实现。

前文并没有讲解算法与数据结构,其实redis里面还是有些这玩意儿的,比如:双向链表(adlist.c,重敲一遍花了2小时),哈希表(dict.c,对其他文件的关联度相对较大,没能重敲完),快速排序(pqsort.c,高度优化,真心有engineering的味道)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值