天天看點

Redis源碼解析(1)——源碼目錄介紹

概念

      redis是一個key-value存儲系統。和Memcached類似,它支援存儲的value類型相對更多,包括string(字元串)、list(連結清單)、set(集合)和zset(有序集合)。這些資料類型都支援push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。與memcached一樣,為了保證效率,資料都是緩存在記憶體中。差別的是redis會周期性的把更新的資料寫入磁盤或者把修改操作寫入追加的記錄檔案,并且在此基礎上實作了master-slave(主從)同步。

開始

<code>$ </code><code>tar</code> <code>xzf redis-2.2.2.</code><code>tar</code><code>.gz</code>

<code>$ </code><code>cd</code> <code>redis-2.2.2</code>

<code>$ </code><code>make</code>

     這裡的make實際上操作的是Makefile檔案,Makefile按類型、功能、子產品分别放在若幹個目錄中,makefile定義了一系列的規則來指定,哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重新編譯,甚至于進行更複雜的功能操作,因為makefile就像一個shell腳本一樣,其中也可以執行作業系統的指令。

浏覽下Redis根目錄中的Makefile檔案:

<a href="http://www.cnblogs.com/liping13599168/archive/2011/04/12/2013094.html#">+ View Code</a>

      通過make指令可以執行“cd src &amp;&amp; make all”。而此時的make all實際上已經開始執行src目錄中的Makefile檔案。這個檔案比較複雜,大緻就是将一系列的c檔案以及h檔案連結起來,通過cc/gcc編譯器将檔案生成目标檔案o,接着将相應的o目标檔案在通過編譯器生成exe檔案,當你編譯完畢後,在src的目錄上将産生5個exe檔案:

Redis源碼解析(1)——源碼目錄介紹

redis-benchmark.exe:用于做性能測試;

redis-check-aof.exe:更新日志檢查;

redis-check-dump.exe:用于本地資料庫檢查;

redis-cli.exe:用戶端程式;

redis-server.exe:服務端程式;

      現在來看下src包含的檔案(我按照首字母順序來講):

adlist.h/adlist.c:用于對list的定義,它是個雙向連結清單結構,從頭檔案可以找到:

<code>// list節點</code>

<code>typedef</code> <code>struct</code> <code>listNode {</code>

<code>    </code><code>struct</code> <code>listNode *prev;</code>

<code>    </code><code>struct</code> <code>listNode *next;</code>

<code>    </code><code>void</code> <code>*value;</code>

<code>} listNode;</code>

<code>// list疊代器</code>

<code>typedef</code> <code>struct</code> <code>listIter {</code>

<code>    </code><code>listNode *next;</code>

<code>    </code><code>int</code> <code>direction;</code>

<code>} listIter;</code>

<code>// list資料結構</code>

<code>typedef</code> <code>struct</code> <code>list {</code>

<code>    </code><code>listNode *head;</code>

<code>    </code><code>listNode *tail;</code>

<code>    </code><code>void</code> <code>*(*dup)(</code><code>void</code> <code>*ptr);</code>

<code>    </code><code>void</code> <code>(*</code><code>free</code><code>)(</code><code>void</code> <code>*ptr);</code>

<code>    </code><code>int</code> <code>(*match)(</code><code>void</code> <code>*ptr, </code><code>void</code> <code>*key);</code>

<code>    </code><code>unsigned </code><code>int</code> <code>len;</code>

<code>} list;</code>

在ListNode節點下包含prev指針和next指針,說明它通過指針将節點進行雙向連結。

并且從adlist.h的頭檔案可以找到非常豐富的方法聲明,包括list建立,list釋放,list頭部/尾部添加節點等等,具體在後面的系列會做出介紹。

ae.h/ae.c:用于Redis的事件處理,包括句柄事件和逾時事件。

在ae.c中的頭部可以發現:

<code>#ifdef HAVE_EPOLL</code>

<code>#include "ae_epoll.c"</code>

<code>#else</code>

<code>    </code><code>#ifdef HAVE_KQUEUE</code>

<code>    </code><code>#include "ae_kqueue.c"</code>

<code>    </code><code>#else</code>

<code>    </code><code>#include "ae_select.c"</code>

<code>    </code><code>#endif</code>

<code>#endif</code>

      在網絡相關操作中,定義了一組公共操作接口: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進行管理:

<code>typedef</code> <code>struct</code> <code>dictEntry {</code>

<code>    </code><code>void</code> <code>*key;</code>

<code>    </code><code>void</code> <code>*val;</code>

<code>    </code><code>struct</code> <code>dictEntry *next;</code>

<code>} dictEntry;</code>

<code>typedef</code> <code>struct</code> <code>dictType {</code>

<code>    </code><code>unsigned </code><code>int</code> <code>(*hashFunction)(</code><code>const</code> <code>void</code> <code>*key);</code>

<code>    </code><code>void</code> <code>*(*keyDup)(</code><code>void</code> <code>*privdata, </code><code>const</code> <code>void</code> <code>*key);</code>

<code>    </code><code>void</code> <code>*(*valDup)(</code><code>void</code> <code>*privdata, </code><code>const</code> <code>void</code> <code>*obj);</code>

<code>    </code><code>int</code> <code>(*keyCompare)(</code><code>void</code> <code>*privdata, </code><code>const</code> <code>void</code> <code>*key1, </code><code>const</code> <code>void</code> <code>*key2);</code>

<code>    </code><code>void</code> <code>(*keyDestructor)(</code><code>void</code> <code>*privdata, </code><code>void</code> <code>*key);</code>

<code>    </code><code>void</code> <code>(*valDestructor)(</code><code>void</code> <code>*privdata, </code><code>void</code> <code>*obj);</code>

<code>} dictType;</code>

<code>typedef</code> <code>struct</code> <code>dict {</code>

<code>    </code><code>dictType *type;</code>

<code>    </code><code>void</code> <code>*privdata;</code>

<code>    </code><code>dictht ht[2];</code>

<code>    </code><code>int</code> <code>rehashidx; </code><code>/* rehashing not in progress if rehashidx == -1 */</code>

<code>    </code><code>int</code> <code>iterators; </code><code>/* number of iterators currently running */</code>

<code>} dict;</code>

這裡dictEntry作為一個dict字段結構,裡面包括key以及value,已經指向下一個dictEntry的指針。dictType作為一些dict的操作結構。dict作為一個hash結構。後面的文章會具體介紹。

fmacros.h:用于Mac下的相容性處理。

help.h:輔助于指令的提示資訊,作用于redis-cli.exe可執行檔案中。

<code>struct</code> <code>commandHelp {</code>

<code>  </code><code>char</code> <code>*name;</code>

<code>  </code><code>char</code> <code>*params;</code>

<code>  </code><code>char</code> <code>*summary;</code>

<code>  </code><code>int</code> <code>group;</code>

<code>  </code><code>char</code> <code>*since;</code>

<code>} commandHelp[] = {</code>

<code>    </code><code>{ </code><code>"APPEND"</code><code>,</code>

<code>    </code><code>"key value"</code><code>,</code>

<code>    </code><code>"Append a value to a key"</code><code>,</code>

<code>    </code><code>1,</code>

<code>    </code><code>"1.3.3"</code> <code>},</code>

<code>    </code><code>{ </code><code>"AUTH"</code><code>,</code>

<code>    </code><code>"password"</code><code>,</code>

<code>    </code><code>"Authenticate to the server"</code><code>,</code>

<code>    </code><code>8,</code>

<code>    </code><code>"0.08"</code> <code>},</code>

<code>    </code><code>{ </code><code>"BGREWRITEAOF"</code><code>,</code>

<code>    </code><code>"-"</code><code>,</code>

<code>    </code><code>"Asynchronously rewrite the append-only file"</code><code>,</code>

<code>    </code><code>9,</code>

<code>    </code><code>....</code>

<code>};</code>

intset.h/intset.c:整數範圍内的使用set,并包含相關set操作。

lzf.h/lzf_c.c/lzf_d.c/lzfP.h:對于本地資料庫的儲存,使用的是LZF壓縮算法,很神奇,算法隻有200-300行的代碼。

multi.c:用于事務處理操作。請看這樣的一個例子:

Redis源碼解析(1)——源碼目錄介紹

通過執行exec,可以送出整個事務過程,如果你想撤銷整個事務過程,你可以使用discard指令:

Redis源碼解析(1)——源碼目錄介紹

可以發現get age已經取不到值了,說明discard指令讓事務失效。

networking.c:網絡協定傳輸方法定義相關的都放在這個檔案裡面了。包括讓Client連接配接上Server,讓Slave挂接到Master,已經Server/Client之間的資訊互動的實作等等。

object.c:用于建立和釋放redisObject對象,redisObject結構為:

<code>typedef</code> <code>struct</code> <code>redisObject {</code>

<code>    </code><code>unsigned type:4;</code>

<code>    </code><code>unsigned storage:2;     </code><code>/* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */</code>

<code>    </code><code>unsigned encoding:4;</code>

<code>    </code><code>unsigned lru:22;        </code><code>/* lru time (relative to server.lruclock) */</code>

<code>    </code><code>int</code> <code>refcount;</code>

<code>    </code><code>void</code> <code>*ptr;</code>

<code>    </code><code>/* VM fields are only allocated if VM is active, otherwise the</code>

<code>     </code><code>* object allocation function will just allocate</code>

<code>     </code><code>* sizeof(redisObjct) minus sizeof(redisObjectVM), so using</code>

<code>     </code><code>* Redis without VM active will not have any overhead. */</code>

<code>} robj;</code>

pqsort.h/pqsort.c/sort.c:關于排序算法,sort.c具體作為Redis場景下的排序實作。

pubsub.c:用于訂閱模式的實作,有點類似于Client廣播發送的方式。

rdb.c:對于Redis本地資料庫的相關操作,預設檔案是dump.rdb(通過配置檔案獲得),包括的操作包括儲存,移除,查詢等等。

redis-benchmark.c:用于redis性能測試的實作。請看main方法以下設定:

<code>config.debug = 0;</code>

<code>config.numclients = 50;</code>

<code>config.requests = 10000;</code>

<code>config.liveclients = 0;</code>

<code>config.el = aeCreateEventLoop();</code>

<code>aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);</code>

<code>config.keepalive = 1;</code>

<code>config.donerequests = 0;</code>

<code>config.datasize = 3;</code>

<code>config.randomkeys = 0;</code>

<code>config.randomkeys_keyspacelen = 0;</code>

<code>config.quiet = 0;</code>

<code>config.loop = 0;</code>

<code>config.idlemode = 0;</code>

<code>config.latency = NULL;</code>

<code>config.clients = listCreate();</code>

<code>config.hostip = </code><code>"127.0.0.1"</code><code>;</code>

<code>config.hostport = 6379;</code>

<code>config.hostsocket = NULL;</code>

<code>parseOptions(argc,argv);</code>

<code>config.latency = zmalloc(</code><code>sizeof</code><code>(</code><code>long</code> <code>long</code><code>)*config.requests);</code>

預設性能測試中的用戶端數量為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:用于對字元串的定義,從頭檔案可以找到:

<code>//字元串</code>

<code>struct</code> <code>sdshdr {</code>

<code>    </code><code>int</code> <code>len;</code>

<code>    </code><code>int</code> <code>free</code><code>;</code>

<code>    </code><code>char</code> <code>buf[];</code>

還可以看到對于字元串的相關操作,包括複制,連接配接,清零等等。

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的源碼實作。

下一篇: 水仙花數