在這篇文章中, 將向大家介紹一種比較合理的 Redis 源碼閱讀順序, 希望可以給對 Redis 有興趣并打算閱讀 Redis 源碼的朋友帶來一點幫助。
第 1 步:閱讀數(shù)據(jù)結(jié)構(gòu)實現(xiàn)
剛開始閱讀 Redis 源碼的時候, 最好從數(shù)據(jù)結(jié)構(gòu)的相關(guān)文件開始讀起, 因為這些文件和 Redis 中的其他部分耦合最少, 并且這些文件所實現(xiàn)的數(shù)據(jù)結(jié)構(gòu)在大部分算法書上都可以了解到, 所以從這些文件開始讀是最輕松的、難度也是最低的。
下表列出了 Redis 源碼中, 各個數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)文件:
文件內(nèi)容
sds.h?和?sds.c????Redis 的動態(tài)字符串實現(xiàn)。
adlist.h?和?adlist.c????Redis 的雙端鏈表實現(xiàn)。
dict.h?和?dict.c????Redis 的字典實現(xiàn)。
redis.h?中的?zskiplist?結(jié)構(gòu)和?zskiplistNode?結(jié)構(gòu), 以及?t_zset.c?中所有以?zsl?開頭的函數(shù), 比如?zslCreate?、?zslInsert、?zslDeleteNode 等等。Redis 的跳躍表實現(xiàn)。
hyperloglog.c?中的?hllhdr?結(jié)構(gòu), 以及所有以?hll?開頭的函數(shù)。Redis 的HyperLogLog 實現(xiàn)。
第 2 步:閱讀內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)實現(xiàn)
在閱讀完和數(shù)據(jù)結(jié)構(gòu)有關(guān)的文件之后, 接下來就應(yīng)該閱讀內(nèi)存編碼(encoding)數(shù)據(jù)結(jié)構(gòu)了。
和普通的數(shù)據(jù)結(jié)構(gòu)一樣, 內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)基本上是獨立的, 不和其他模塊耦合, 但是區(qū)別在于:
上一步要讀的數(shù)據(jù)結(jié)構(gòu), 比如雙端鏈表、字典、HyperLogLog, 在算法書上或者相關(guān)的論文上都可以找到資料介紹。
而內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)卻不容易找到相關(guān)的資料, 因為這些數(shù)據(jù)結(jié)構(gòu)都是 Redis 為了節(jié)約內(nèi)存而專門開發(fā)出來的, 換句話說, 這些數(shù)據(jù)結(jié)構(gòu)都是特制(adhoc)的, 除了 Redis 源碼中的文檔之外, 基本上找不到其他資料來了解這些特制的數(shù)據(jù)結(jié)構(gòu)。
不過話又說回來, 雖然內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)是 Redis 特制的, 但它們基本都和內(nèi)存分配、指針操作、位操作這些底層的東西有關(guān), 讀者只要認真閱讀源碼中的文檔, 并在有需要時, 畫圖來分析這些數(shù)據(jù)結(jié)構(gòu), 那么要完全理解這些內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)的運作原理并不難, 當然這需要花一些功夫。
下表展示了 Redis 源碼中, 各個內(nèi)存編碼數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)文件:
文件內(nèi)容
intset.h?和?intset.c整數(shù)集合(intset)數(shù)據(jù)結(jié)構(gòu)。
ziplist.h?和?ziplist.c壓縮列表(zip list)數(shù)據(jù)結(jié)構(gòu)。
第 3 步:閱讀數(shù)據(jù)類型實現(xiàn)
在完成以上兩個閱讀步驟之后, 我們就讀完了 Redis 六種不同類型的鍵(字符串、散列、列表、集合、有序集合、HyperLogLog)的所有底層實現(xiàn)結(jié)構(gòu)了。
接下來, 為了知道 Redis 是如何通過以上提到的數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)不同類型的鍵, 我們需要閱讀實現(xiàn)各個數(shù)據(jù)類型的文件, 以及 Redis 的對象系統(tǒng)文件, 這些文件包括:
文件內(nèi)容
object.c? ? Redis 的對象(類型)系統(tǒng)實現(xiàn)。
t_string.c????字符串鍵的實現(xiàn)。
t_list.c????列表鍵的實現(xiàn)。
t_hash.c????散列鍵的實現(xiàn)。
t_set.c????集合鍵的實現(xiàn)。
t_zset.c?????中除?zsl?開頭的函數(shù)之外的所有函數(shù)。有序集合鍵的實現(xiàn)。
hyperloglog.c?????中所有以?pf?開頭的函數(shù)。HyperLogLog 鍵的實現(xiàn)。
第 4 步:閱讀數(shù)據(jù)庫實現(xiàn)相關(guān)代碼
在讀完了 Redis 使用所有底層數(shù)據(jù)結(jié)構(gòu), 以及 Redis 是如何使用這些數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)不同類型的鍵之后, 我們就可以開始閱讀 Redis 里面和數(shù)據(jù)庫有關(guān)的代碼了, 它們分別是:
文件內(nèi)容
redis.h?????文件中的?redisDb?結(jié)構(gòu), 以及?db.c?文件。Redis 的數(shù)據(jù)庫實現(xiàn)。
notify.c????Redis 的數(shù)據(jù)庫通知功能實現(xiàn)代碼。
rdb.h?和?rdb.c????Redis 的 RDB 持久化實現(xiàn)代碼。
aof.c????Redis 的 AOF 持久化實現(xiàn)代碼。
選讀
Redis 有一些獨立的功能模塊, 這些模塊可以在完成第 4 步之后閱讀, 它們包括:
文件內(nèi)容
redis.h?文件的?pubsubPattern?結(jié)構(gòu),以及?pubsub.c?文件。發(fā)布與訂閱功能的實現(xiàn)。
redis.h?文件的?multiState?結(jié)構(gòu)以及?multiCmd?結(jié)構(gòu),?multi.c?文件。事務(wù)功能的實現(xiàn)。
sort.c????SORT?命令的實現(xiàn)。
bitops.c????GETBIT?、?SETBIT?等二進制位操作命令的實現(xiàn)。
第 5 步:閱讀客戶端和服務(wù)器的相關(guān)代碼
在閱讀完數(shù)據(jù)庫實現(xiàn)代碼, 以及 RDB 和 AOF 兩種持久化的代碼之后, 我們可以開始閱讀客戶端和 Redis 服務(wù)器本身的實現(xiàn)代碼, 和這些代碼有關(guān)的文件是:
文件內(nèi)容
事件處理模塊 ae.c/ae_epoll.c/ae_evport.c/ae_kqueue.c/ae_select.c。以及任意一個?ae_*.c?文件(取決于你所使用的多路復用庫)。Redis 的事件處理器實現(xiàn)(基于 Reactor 模式)。
網(wǎng)路鏈接庫 anet.c和networking.c的網(wǎng)絡(luò)連接庫,負責發(fā)送命令回復和接受命令請求, 同時也負責創(chuàng)建/銷毀客戶端, 以及通信協(xié)議分析等工作。
服務(wù)器端 redis.c客戶端 redis-cli.c
如果讀者能完成以上 5 個閱讀步驟的話, 那么恭喜你, 你已經(jīng)了解了單機的 Redis 服務(wù)器是怎樣處理命令請求和返回命令回復, 以及是 Redis 怎樣操作數(shù)據(jù)庫的了, 這是 Redis 最重要的部分, 也是之后繼續(xù)閱讀多機功能的基礎(chǔ)。
選讀
Redis 有一些獨立的功能模塊, 這些模塊可以在完成第 5 步之后閱讀, 它們包括:
文件內(nèi)容
scripting.c????Lua 腳本功能的實現(xiàn)。slowlog.c????慢查詢功能的實現(xiàn)。
monitor.c????監(jiān)視器功能的實現(xiàn)。
第 6 步:閱讀多機功能的實現(xiàn)
在弄懂了 Redis 的單機服務(wù)器是怎樣運作的之后, 就可以開始閱讀 Redis 多機功能的實現(xiàn)代碼了, 和這些功能有關(guān)的文件為:
文件內(nèi)容
replication.c????復制功能的實現(xiàn)代碼。
sentinel.c????Redis Sentinel 的實現(xiàn)代碼。
cluster.c????Redis 集群的實現(xiàn)代碼。
注意, 因為 Redis Sentinel 用到了復制功能的代碼, 而集群又用到了復制和 Redis Sentinel 的代碼, 所以在閱讀這三個模塊的時候, 記得先閱讀復制模塊, 然后閱讀 Sentinel 模塊, 最后才閱讀集群模塊, 這樣理解起來就會更得心應(yīng)手。
如果你連這三個模塊都讀完了的話, 那么恭喜你, 你已經(jīng)讀完了 Redis 單機功能和多機功能的所有代碼了!
其他代碼文件介紹
關(guān)于測試方面的文件有:
memtest.c ????內(nèi)存檢測
redis_benchmark.c ????用于redis性能測試的實現(xiàn)。
redis_check_aof.c ????用于更新日志檢查的實現(xiàn)。
redis_check_dump.c ????用于本地數(shù)據(jù)庫檢查的實現(xiàn)。
testhelp.c ????一個C風格的小型測試框架。
一些工具類的文件如下:
bitops.c ????GETBIT、SETBIT 等二進制位操作命令的實現(xiàn)
debug.c ????用于調(diào)試時使用
endianconv.c ????高低位轉(zhuǎn)換,不同系統(tǒng),高低位順序不同
help.h ????輔助于命令的提示信息
lzf_c.c ????壓縮算法系列
lzf_d.c ????壓縮算法系列
rand.c ????用于產(chǎn)生隨機數(shù)
release.c ????用于發(fā)布時使用
sha1.c sha????加密算法的實現(xiàn)
util.c ????通用工具方法
crc64.c ????循環(huán)冗余校驗
sort.c ????SORT命令的實現(xiàn)
一些封裝類的代碼實現(xiàn):
bio.c ????background I/O的意思,開啟后臺線程用的
latency.c ????延遲類
migrate.c ????命令遷移類,包括命令的還原遷移等
pqsort.c ????排序算法類
rio.c ????redis定義的一個I/O類
syncio.c ????用于同步Socket和文件I/O操作
下圖總結(jié)了本文介紹的閱讀順序:

結(jié)語
Redis 的設(shè)計非常簡潔、優(yōu)美、精巧和高效, 任何人只要愿意去閱讀它的代碼的話, 應(yīng)該都會有所收獲的。
希望這篇文章能夠給想要閱讀 Redis 代碼的朋友們帶來一些幫助, 也歡迎各位隨時留言討論 Redis 源碼方面的問題, 或者跟我分享各位閱讀 Redis 源碼的心得和經(jīng)驗。