Redis原始碼系列之從何讀起

2022-06-09 14:03:09 字數 3132 閱讀 2457

看過很多資料介紹redis原始碼的閱讀方法,總結起來主流的大概有兩種:

2、由於redis原始碼用c寫成,直接從main()函式入手,逐步了解大概的處理框架和相關機制。類似於從主幹到分枝的方法。

兩種方法能彙總起來當然是最好,但花費精力巨大。讀者可根據實際情況選擇閱讀方式。本系列將採用第二種方法簡略記錄閱讀心得(原始碼版本選自5.0.4)。

上文提到redis原始碼由c寫成,可有main()函式入手,檢視每乙個命令輸入之後執行經過的詳細過程,這樣我們可以從外部輸入一步步了解內在邏輯。

比如乙個ping命令從main()進入後到了processcommand過程 ,其中有宣告命令表rediscommandtable,rediscommandtable表中有所有的redis命令和相應的引數,

接著可以檢視pingcommand命令,發現該命令十分簡單,直接返回共享物件字元「pong」,接著我們可以去檢視共享字元「pong」的定義方式。乙個最簡單的命令執行邏輯大概就清楚了。

我們還可以根據這個方法在redis裡實現程式世界裡最簡單的「hello,world」,在這裡不做細述,感興趣可以參考我的另一篇文章

經常能聽到說redis是單執行緒模型(不考慮redis6.0多執行緒io),這話並不完全正確,如果看過原始碼,可以知道在主線程外還有多個後台執行緒(比如lazy-free和持久化子程序,詳見bio.h和bio.c)。

主線程main函式的邏輯主要包括兩個部分:

1、各種初始化,包括載入配置檔案、初始化命令表、資料庫服務等

2、執行事件迴圈

我們通過原始碼細看各個環節的具體流程

1、配置載入和初始化:redis會通過預設引數去初始化,並將其中已經修改掉的引數覆蓋預設配置。其中涉及到的內容有用來載入配置引數的loadserverconfig函式、用來初始化所有redis命令的populatecommandtable函式、初始化哨兵的initsentinelconfig函式等等。redis服務是有redisserver的全域性變數來表示的,變數裡包含了上百個變數值和識別符號,比如動態hz、配置檔案等等。

2、建立事件迴圈。事件迴圈是由aeeventloop來表示的,事件迴圈依賴作業系統底層的i/o多路復用機制。為什麼redis是單執行緒執行卻能同時處理多個請求這是redis相關工作人員無法迴避的話題,在這個問題的答案中,i/o多路復用不可或缺,關於i/o多路復用,有個知乎回答我覺得講的挺清楚,可以檢視鏈結io 多路復用是什麼意思? - 羅志宇的回答 - 知乎

3、開始socket監聽。redis監聽模式分為兩種:tcp和ipc(unix domain socket)。ipc是一種高效的程序間的通訊機制,在同伺服器中的不同程序有著更高的效能。而在一般的業務場景中,服務和資料庫都部署在不同的伺服器上,因此大部分業務都選擇tcp。

4、註冊時間事件**。redis作為乙個單執行緒程式,如果想呼叫非同步程式比如週期性的過期key(expire)、降低碎片率(defrag)、重置部分連線、漸進式rehash等等,除了依賴主事件迴圈沒有其他的辦法。在這個過程中會註冊乙個時間事件,形成週期性執行的**函式servercron。它會在合適的時間呼叫(比如過期key超過了百分比閾值,aof檔案大小觸發了rewrite機制)

5、註冊i/o事件**。redis服務端最主要的工作就是監聽i/o事件,從中分析出來自客戶端的命令請求,執行命令,然後返回響應結果。對於i/o事件的監聽,自然也是依賴事件迴圈。前面提到過,redis可以開啟兩種監聽:對於tcp連線的監聽和對於unix domain socket的監聽。因此,這裡就包含對於這兩種i/o事件的**的註冊,兩個**函式分別是accepttcphandleracceptunixhandler。對於來自redis客戶端的請求的處理,就會走到這兩個函式中去。我們在下一部分就會討論到這個處理過程

6、初始化後台執行緒。redis會建立一些額外的執行緒,在後台執行,專門用於處理一些耗時的並且可以被延遲執行的任務,比如刪除大key(lazy free機制,aof重寫等等)。

7、啟動事件迴圈。這一步主要建立了乙個死迴圈,可以保證前面註冊的timer事件**和i/o事件**不斷執行。

redis處理命令的詳細流程見下圖:

整個過程主要有兩步:

1、建立連線:客戶端發起連線請求(通過tcp或unix domain socket),伺服器接收連線;

建立連線的邏輯:accepttcphandler-> acceptcommonhandler

accepttcphandler:在初始化時會建立i/o事件**,將定時呼叫這個**函式,用於接收client socket連線;

acceptcommonhandler:初始化客戶端(createclient,初始化redisclient資料結構),判斷連線數是否超出maxclients;

2、命令傳送、執行和響應:客戶端在新建連線上傳送命令資料,伺服器接收執行命令,並把結果返回客戶端;為了在新的連線上能夠接收到客戶端發來的命令,需要在事件迴圈中為新建連線的檔案描述符註冊乙個i/o事件**:readqueryfromclient ,負責將從客戶端讀取的資料累積到查詢緩衝區中,對應執行和相應。

從讀取query buffer到處理命令的處理邏輯:readqueryfromclient-> processinputbufferandreplicate-> processinputbuffer-> processcommand-> call

readqueryfromclient函式:讀取客戶端資料,儲存到query buffer;

processinputbufferandreplicate函式:processinputbuffer函式包裝了一下,主要用於判斷客戶端是不是master客戶端;

processinputbuffer函式:根據redis協議解析客戶端qeury buffer;

processcommand函式:搜尋匹配命令表,做一些邊界檢查;

call函式:最終執行命令的函式;

1、從main函式啟動後的初始化過程;

2、事件迴圈的執行邏輯和原理;

3、乙個redis命令從請求接收,到命令的解析和執行,再到執行結果返回的完整過程。

另有redis命令呼叫關係圖由於排版問題無法展示,有興趣可小窗博主。

Redis原始碼分析系列

redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...

redis原始碼之dict

大家都知道redis預設是16個db,但是這些db底層的設計結構是什麼樣的呢?我們來簡單的看一下原始碼,重要的字段都有所注釋 typedef struct redisdb redisdb redis中的所有kv都是存放在dict中的,dict型別在redis中非常重要。字典disc的資料結構如下 t...

Redis 原始碼學習之 Redis 事務Nosql

redis事務提供了一種將多個命令請求打包,然後一次性 按照順序地執行多個命令的機制,並且在事務執行的期間,伺服器不會中斷事務而去執行其他不在事務中的命令請求,它會把事務中所有的命令都執行完畢才會去執行其他的命令。howredis中提供了multi discard exec watch unwatc...