標籤:
一、前言
因為近期項目中開始使用Redis,為了更好的理解Redis並應用在適合的業務情境,需要對Redis設計與實現深入的理解。我分析流程是按照從main進入,逐步深入分析Redis的啟動流程。同時根據Redis初始化的流程,理解Redis各個模組的功能及原理。
二、redis啟動流程 1.初始化server變數,設定redis相關的預設值2.讀入設定檔,同時接收命令列中傳入的參數,替換伺服器設定的預設值3.初始化伺服器功能模組。在這一步初始化了包括進程訊號處理、用戶端鏈表、共用對象、初始化資料、初始化網路連接等4.從RDB或AOF重載資料5.網路監聽服務啟動前的準備工作6.開啟事件監聽,開始接受用戶端的請求
啟動的部分過程通過查看,會更直觀。
下面是針對啟動過程中,對各個模組的詳細理解。(目前只分析了後台線程系統與慢查詢日誌系統)
三、Redis資料持久化方案 在使用redis時不少人都說一個問題,就是說redis宕機了怎麼辦?會不會資料丟失等等的問題。現在來看看Redis提供的資料持久化解決方案,並通過原理分析優缺點。最終能得出Redis適合使用的應用情境。
1.RDB持久化方案在Redis運行時,RDB程式將當前記憶體中的資料庫快照集儲存到磁碟中,當Redis需要重啟時,RDB程式會通過重載RDB檔案來還原資料庫。從上述描述可以看出,RDB主要包括兩個功能:關於rdb的實現可以見src/rdb.ca)儲存(rdbSave)
rdbSave負責將記憶體中的資料庫資料以RDB格式儲存到磁碟中,如果RDB檔案已經存在將會替換已有的RDB檔案。儲存RDB檔案期間會阻塞主進程,這段時間期間將不能處理新的用戶端請求,直到儲存完成為止。為避免主進程阻塞,Redis提供了rdbSaveBackground函數。在建立的子進程中調用rdbSave,儲存完成後會向主進程發送訊號,同時主進程可以繼續處理新的用戶端請求。
b)讀取(rdbLoad)
當Redis啟動時,會根據配置的持久化模式,決定是否讀取RDB檔案,並將其中的對象儲存到記憶體中。載入RDB過程中,每載入1000個鍵就處理一次已經等待處理的用戶端請求,但是目前僅處理訂閱功能的命令(PUBLISH 、 SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE 、 PUNSUBSCRIBE),其他一律返回錯誤資訊。因為發布訂閱功能是不寫入資料庫的,也就是不儲存在Redis資料庫的。
RDB的缺點:再說RDB缺點時,需要提到的是RDB有儲存點的概念。在預設的redis.conf中可以看到這樣的預設配置:
[plain] view plaincopy
- #save <seconds> <changes>
[plain] view plaincopy
- save 900 1 #如果15分鐘內,有1個鍵被修改
[plain] view plaincopy
- save 300 10 #如果6分鐘內,有10個鍵被修改
[plain] view plaincopy
- save 60 10000 #如果60秒內有10000個鍵被修改
意思是當滿足上面任意一個條件時,將會進行快照儲存。為了保證IO讀寫效能不會成為Redis的瓶頸,一般都會建立一個比較大的值來作為儲存點。1.此時如果儲存點設定過大,就會導致宕機丟失的資料過多。儲存點設定過小,又會造成IO瓶頸2.當對資料進行儲存時,可能會由於資料集過大導致操作耗時,這會導致Redis可能在短時間內無法處理用戶端請求。
2.AOF持久化方案以協議文本的方式,將所有對資料庫進行的寫入命令記錄到AOF檔案,達到記錄資料庫狀態的目的。a)儲存
1.將用戶端請求的命令轉換為網路通訊協定格式2.將協議內容字串追加到變數server.aof_buf中3.當AOF系統達到設定的條件時,會調用aof_fsync(檔案描述符號)將資料寫入磁碟
其中第三步提到的設定條件,就是AOF效能的關鍵點。目前Redis支援三種儲存條件機制:
1.AOF_FSYNC_NO:不儲存
此模式下,每執行一條用戶端的命令,都會將協議字串追加到server.aof_buf中,但不會執行寫入磁碟。
寫入只發生在:
1.Redis被正常關閉
2.Aof功能關閉
3.系統寫緩衝已滿,或後台定時儲存操作被執行
上面三種情況都會阻塞主進程,導致用戶端請求失敗。
2.AOF_FSYNC_EVERYSECS:每一秒儲存一次
由後檯子進程調用寫入儲存,不會阻塞主進程。如果發生宕機,那麼最大遺失資料會在2s以內的資料。
這也是預設的設定選項
3.AOF_FSYNC_ALWAYS:每執行一個命令都儲存一次
這種模式下,可以保證每一條用戶端指令都被儲存,保證資料不會丟失。但缺點就是效能大大下降,因為每一次操作都是獨佔性的,需要阻塞主進程。
b)讀取
AOF儲存的是資料協議格式的資料,所以只要將AOF中的資料轉換為命令,類比用戶端重新執行一遍,就可以還原所有資料庫狀態。讀取的過程是:1.建立類比的用戶端2.讀取AOF儲存的文本,還原資料為原命令和原參數。然後使用類比的用戶端發出這個命令請求。3.繼續執行第二步,直到讀取完AOF檔案
AOF需要將所有的命令都儲存到磁碟,那麼這個檔案會隨著時間變得越來越大。讀取也會變得很慢。Redis提供了AOF的重寫機制,協助減少檔案的大小。實現的思路是:
[plain] view plaincopy
- LPUSH list 1 2 3 4 5
[plain] view plaincopy
- LPOP list
[plain] view plaincopy
- LPOP list
[plain] view plaincopy
- LPUSH list 1
最初儲存到AOF檔案的將會是四條指令。但經過AOF重寫後,會變成一條指令:
[plain] view plaincopy
- LPUSH list 1 3 4 5
同時,考慮到為了在AOF重寫時,不影響AOF的寫入增加了AOF重寫緩衝的概念。也就是說Redis在開啟AOF時,除了將命令格式資料寫入到AOF檔案,同時也會寫入到AOF重寫緩衝。這樣AOF的寫入、重寫就做到了隔離,保證了重寫時不會阻塞寫入。
c)AOF重寫流程
1.AOF重寫完成會向主進程發送一個完成的訊號2.會將AOF重寫緩衝中的資料全部寫入到檔案中 3.用新的AOF檔案,覆蓋原有的AOF檔案。
d)AOF缺點
1.AOF檔案通常會大於相同資料集的RDB檔案2.AOF模式下效能與RDB模式下效能高低,主要取決於AOF選用的fsync模式
下面給出用戶端請求RedisServer時,server端持久化的部分操作圖解。
四、Redis資料庫的實現
Redis是一個索引值對資料庫,稱為鍵空間。實現這種KV形式的儲存,Redis使用了兩種資料結構類型:1、字典
Redis字典使用的是雜湊表實現,原本不準備詳細介紹Redis雜湊表的實現。但發現Redis在實現雜湊表時,
提供了一個很好的rehash方案,這個方案思路很好,甚至可以衍生到其他各個應用中使用,方案的名稱叫“漸進式Rehash”。
實現雜湊表的方法大同小異,但為何各個開源軟體總是去開發自己專屬的雜湊資料結構呢?
從研究PHP核心的雜湊實現與Redis雜湊實現,發現應用情境決定了必須定製才能更好的發揮效能。(關於PHP雜湊實現可以參看:PHP核心中的神器之HashTable)
a)PHP主要應用於WEB情境,在WEB情境針對單次請求資料之間是隔離的,並且雜湊的數量是有限的,那麼進行一次rehash也是很快的。
所以PHP核心使用阻塞形式rehash,即rehash進行中將不能對當前雜湊表進行任何操作。
b)在來看Redis,常駐進程,接收用戶端請求處理各項事務,並且操作的資料是相關且資料量較大的,如果使用PHP核心的那種方式就會出現:
對雜湊表進行rehash時,此時將阻塞所有用戶端請求,並發效能會大大下降。
初始化字典圖解:
新增字典元素圖解:
Rehash執行流程:
分析Redis架構設計