Redis源码解析——前言
? ? ? ??今天開啟Redis源碼的閱讀之旅。對于一些沒有接觸過開源代碼分析的同學來說,可能這是一件很麻煩的事。但是我總覺得做一件事,不管有多大多難,我們首先要在戰略上蔑視它,但是要在戰術上重視它。除了一些高大上的技術,我們一般人都能用比較簡單的方式描述它是干什么的。比如Redis,它不就是一個可以通過網絡訪問的KV型數據庫嘛。在沒有源碼的情況下,可以想象出它應該是通過網絡服務、指令解析、特殊的內存結構設計(方便增刪改查)、持久化等技術構成。然后我們在戰術上要重視它各個技術的實現,特別是一些我們沒想到的一些技術。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 首先,我會先粗略閱讀一遍代碼。這個過程不會花費比較多的時間,因為只是簡單看看。很多技術細節、算法等都是一帶而過。這個過程只為了達到一個效果:大致清楚每個文件都是為了解決哪類問題的。其實第一遍“掃”代碼并不能完全達到這個效果,但是能預判出我們確定需要使用的技術對應的文件就行了。剩下的一些未知文件可能就是我們在預估這個工程構成時沒有考慮到的,它們往往隱藏了一些特殊功能。
? ? ? ? 然后,我會選擇閱讀Makefile文件,看看它是由哪些文件編譯生成,以及它依賴的一些技術。再之后就按我們預估的技術點去看看它們的實現。在閱讀的過程中,我們可能會遇到之前我們沒有預估到的一些技術點,這個時候我們要采用廣度優先方法去閱讀還是深度優先方法去閱讀就要看各自的技術能力和愛好了。
? ? ? ? 現在回到Redis上來。我準備閱讀的源碼地址是http://download.redis.io/releases/redis-3.2.5.tar.gz。它是目前穩定的3.2版本。解壓完之后,可以發現
? ? ? ??一般來說,我們可以通過文件命名猜測出它們的功能。但是閱讀過程也是要“大膽猜測,小心求證”。比如上圖中,Copying文件可以猜測出它是版本信息文件,我們可以不關心。Runtest、Runtest-cluster、Runtest-sentinel分別對應于“整體測試”、“分布式測試”和“主從切換測試”的腳本,這個時候我們就知道“分布式”和“主從切換”是Redis的重要功能,這樣就補齊了我們對其技術點構成的認識。Redis.conf和Seninel.conf分別對應于Redis的配置和主從配置,此時看這些配置還沒用,前期我們可以略過,等到用到時再回來看看。Deps應該是depends的英文縮寫,即它應該是依賴的庫
? ? ? ??一般來說,依賴庫都是開源的第三方庫。上圖可見Redis需要在內部使用到:
- Lua腳本引擎。Redis內嵌Lua腳本引擎,那么說明Redis需要Lua語言的解析能力。那么可以進一步猜測應該是用戶可以定制Lua腳本讓Reids去執行,這相當于Redis開放了一個非常自由的接口供外部使用。
- Linenoise是一個命令行編輯庫。這個正是我們之前預估的Redis基礎功能之一。它的相關資料可見https://github.com/antirez/linenoise
- Jemalloc是內存管理庫。很多開源項目不使用glibc自帶的ptmalloc,而是使用Jemalloc或者Tcmalloc這類更高效的內存管理庫。
- Hiredis是Redis數據庫的C接口。這塊和Redis相關性比較大,我們之后也會重點關注下。
- Geohash-int是一種地理編碼算法。它將二維經緯度信息轉換成Int型數據。
? ? ? ? 上面這些第三方庫,我們不會全部去看。比如Lua腳本引擎,這塊技術非常獨立,我們在閱讀Redis代碼時應該是不會深入閱讀的。再比如Jemalloc庫,它也是非常基礎的庫,未來我應該會分析ptmalloc、tcmalloc和jemalloc這三種內存管理庫,但是在分析Redis代碼時也不會去深入閱讀。Geohash-int是一套算法,除非它提供的特性對redis非常重要,否則之后應該也不會去閱讀。Linenoise是用于命令行編輯的,它也非Redis主要功能,可以不用去看。Hiredis可能和Redis的相關性大一些,這個模塊應該會被關注。
? ? ? ? 退到上一層,再看看Tests,它是測試相關的目錄。里面都是各種測試Redis的腳本。
? ? ? ??可見測試腳本是一些后綴為tcl的文件。它的內容這是一種被廣泛使用的腳本測試語言——TCL語言。這塊我們應該也不會過多涉及。
? ? ? ??Utils從命名看,它應該是一些工具
? ? ? ??從上圖看,我們可以發現其內容大部分是一些腳本語言。所以我們之后也不會太多涉及。
? ? ? ? 然后我們關注下Makefile文件
# Top level makefile, the real shit is at src/Makefiledefault: all.DEFAULT:cd src && $(MAKE) $@install:cd src && $(MAKE) $@.PHONY: install
? ? ? ??它進入到src目錄,然后再make。于是我們也進入最最重要的redis源碼目錄——src去一看究竟。
? ? ? ? 進入Src后,我們仍然關注Makffile文件。最開始除了一些編譯參數和依賴項定義外,還有就是內存管理庫的使用問題
# Default allocator
ifeq ($(uname_S),Linux)MALLOC=jemalloc
elseMALLOC=libc
endif# Backwards compatibility for selecting an allocator
ifeq ($(USE_TCMALLOC),yes)MALLOC=tcmalloc
endififeq ($(USE_TCMALLOC_MINIMAL),yes)MALLOC=tcmalloc_minimal
endififeq ($(USE_JEMALLOC),yes)MALLOC=jemalloc
endififeq ($(USE_JEMALLOC),no)MALLOC=libcendif
? ? ? ??linux系統上,Redis默認選擇的內存管理庫是jemalloc,其他系統則是選擇libc的ptmalloc。當然還可以通過指定庫來修改內存管理庫。如上可以見我們還可以選擇tcmalloc或者tcmalloc_minimal。
? ? ? ? 之后還是一些編譯對象組合
REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o
REDIS_GEOHASH_OBJ=../deps/geohash-int/geohash.o ../deps/geohash-int/geohash_helper.o
REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
REDIS_BENCHMARK_NAME=redis-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o zmalloc.o redis-benchmark.o
REDIS_CHECK_RDB_NAME=redis-check-rdb
REDIS_CHECK_AOF_NAME=redis-check-aof
REDIS_CHECK_AOF_OBJ=redis-check-aof.o
…………………………………………
# redis-server
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(REDIS_GEOHASH_OBJ) $(FINAL_LIBS)# redis-sentinel
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)# redis-check-rdb
$(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)# redis-cli
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)# redis-benchmark
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)# redis-check-aof
$(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
? ? ? ??上面腳本可以見這個Makefile可以編譯出6個不同的最終產物。
? ? ? ? 其中最核心的應該是REDIS_SERVER_NAME對應的編譯內容。我們從其需要鏈接的文件(REDIS_SERVER_OBJ中的內容)來看,程序的入口函數main應該位于server.o文件中。我們繼續查看server.c文件,果然發現了它。
? ? ? ? 之后的代碼我采用深度優先的方法去閱讀,但是這種方式是需要在一條主線的基礎之上進行的。這樣就會導致主線的邏輯被打的很零散。所以我決定還是要分章節,從最基礎的一些代碼開始分析。如果模塊和Redis不是強關聯的,我將以該模塊名為分析博文的標題,比如之前介紹的SDS字符串管理庫,它的相關介紹名稱為《Simple Dynamic Strings(SDS)源碼解析和使用說明一》和《Simple Dynamic Strings(SDS)源碼解析和使用說明二》。而和Redis強關聯的模塊,我將以《Redis源碼解析——XXXXX》形式命名。
總結
以上是生活随笔為你收集整理的Redis源码解析——前言的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Simple Dynamic Strin
- 下一篇: Redis源码解析——内存管理