redis——持久化
因為redis是內存數據庫,他把數據都存在內存里,所以要想辦法實現持久化功能。
RDB
RDB持久化可以手動執行,也可以配置定期執行,可以把某個時間的數據狀態保存到RDB文件中,反之,我們可以用RDB文件還原數據庫狀態。
? ? 生成
有兩個命令可以生成RDB文件:
- SAVE?命令由服務器進程直接執行保存操作,所以該命令會阻塞服務器,服務器不能接受其他指令。
- BGSAVE?命令由子進程執行保存操作,所以該命令不會阻塞服務器,服務器可以接受其他指令。。
禁止BGSAVE和SAVE同時執行,也就是說執行其中一個就會拒絕另一個,這是為了避免父進程和子進程同時執行兩個rdbsave,防止產生競爭條件。
? ? 載入
? ? RDB載入工作是服務器啟動時自動執行的。
? ? 自動保存
用戶可以通過save選項設置多個保存條件,服務器狀態中會保存所有用?save?選項設置的保存條件,當任意一個保存條件被滿足時,服務器會自動執行?BGSAVE?命令。
比如
save 900 1
save 300 10
滿足:服務器在900秒之內被修改至少一次或者300秒內修改至少十次。就會執行BGSAVE。
?
當服務器啟動時,用戶可以通過指定配置文件或者傳入啟動參數來設置save選項,服務器會把條件放到一個結構體里,結構體有一個數組,保存了所有條件。
?
serverCron函數默認100毫秒檢查一次,他會遍歷數組依次檢查,符合條件就會執行BGSAVE。
? ? RDB文件結構
一個完整 RDB 文件所包含的各個部分:
REDIS,長度5字節, 保存著?"REDIS"?五個字符。 通過這五個字符, 可以在載入文件時, 快速檢查載入文件是否 RDB 文件。
db_version?,長度?4?字節, 它的值是一個字符串表示的整數, 這個整數記錄了 RDB 文件的版本號
databases?部分包含著零個或任意多個數據庫, 以及各個數據庫中的鍵值對數據
EOF?常量的長度為?1?字節, 這個常量標志著 RDB 文件正文內容的結束
check_sum?是一個?8?字節長的無符號整數, 保存著一個校驗和,以此來檢查 RDB 文件是否出錯或損壞
我并不想深入探究databases的組成。就是知道
- RDB 文件是一個經過壓縮的二進制文件,由多個部分組成。
- 對于不同類型的鍵值對, RDB 文件會使用不同的方式來保存它們。
即可。
?
AOF
AOF持久化是通過保存服務器執行的命令來記錄狀態的。還原的時候再執行一遍即可。
功能的實現可以分為命令追加、文件寫入、文件同步三個步驟。
?
當 AOF 持久化功能處于打開狀態時, 服務器在執行完一個寫命令之后, 會以協議格式將被執行的寫命令追加到服務器狀態的?aof_buf?緩沖區的末尾:
struct redisServer {// ...// AOF 緩沖區sds aof_buf;// ... };Redis 服務器進程就是一個事件循環
循環中的文件事件負責接收客戶端的命令請求, 以及向客戶端發送命令回復,
而時間事件則負責執行像?serverCron?函數這樣需要定時運行的函數。
因為服務器在處理文件事件時可能會執行寫命令, 使得一些內容被追加到?aof_buf?緩沖區里面, 所以在服務器每次結束一個事件循環之前, 它都會調用?flushAppendOnlyFile?函數, 考慮是否需要將?aof_buf?緩沖區中的內容寫入和保存到 AOF 文件里面, 這個過程可以用偽代碼表示:
def eventLoop():while True:# 處理文件事件,接收命令請求以及發送命令回復# 處理命令請求時可能會有新內容被追加到 aof_buf 緩沖區中processFileEvents()# 處理時間事件processTimeEvents()# 考慮是否要將 aof_buf 中的內容寫入和保存到 AOF 文件里面flushAppendOnlyFile()flushAppendOnlyFile?函數的行為由服務器配置的?appendfsync?選項的值來決定
值為?always?時, 服務器在每個事件循環都要將?aof_buf?緩沖區中的所有內容寫入到 AOF 文件并且同步 AOF 文件, 所以?always?的效率最慢的一個, 但從安全性來說,?always?是最安全的, 因為即使出現故障停機, AOF 持久化也只會丟失一個事件循環中所產生的命令數據。
值為?everysec?時, 服務器在每個事件循環都要將?aof_buf?緩沖區中的所有內容寫入到 AOF 文件, 每隔超過一秒就要在子線程中對 AOF 文件進行一次同步: 從效率上來講,?everysec?模式足夠快, 并且就算出現故障停機, 數據庫也只丟失一秒鐘的命令數據。
值為?no?時, 服務器在每個事件循環都要將?aof_buf?緩沖區中的所有內容寫入到 AOF 文件, 至于何時對 AOF 文件進行同步, 則由操作系統控制。
因為處于?no?模式下的?flushAppendOnlyFile?調用無須執行同步操作, 所以該模式下的 AOF 文件寫入速度總是最快的, 不過因為這種模式會在系統緩存中積累一段時間的寫入數據, 所以該模式的單次同步時長通常是三種模式中時間最長的: 從平攤操作的角度來看,no?模式和?everysec?模式的效率類似, 當出現故障停機時, 使用?no?模式的服務器將丟失上次同步 AOF 文件之后的所有寫命令數據。
? ? 重寫
AOF持久化是保存了一堆命令來恢復數據庫,隨著時間流逝,存的會越來越多,如果不加以控制,文件過大可能影響服務器甚至計算機。而且文件過大,恢復時需要時間也太長。
所以redis提供了重寫功能,寫出的新文件不會包含任何浪費時間的冗余命令。
接下來,我們就介紹重寫的原理。
其實重寫不會對現有的AOF文件進行讀取分析等操作,而是通過當前服務器的狀態來實現。
# 假設服務器對鍵list執行了以下命令s; 127.0.0.1:6379> RPUSH list "A" "B" (integer) 2 127.0.0.1:6379> RPUSH list "C" (integer) 3 127.0.0.1:6379> RPUSH list "D" "E" (integer) 5 127.0.0.1:6379> LPOP list "A" 127.0.0.1:6379> LPOP list "B" 127.0.0.1:6379> RPUSH list "F" "G" (integer) 5 127.0.0.1:6379> LRANGE list 0 -1 1) "C" 2) "D" 3) "E" 4) "F" 5) "G" 127.0.0.1:6379>當前列表鍵list在數據庫中的值就為["C", "D", "E", "F", "G"]。要使用盡量少的命令來記錄list鍵的狀態,最簡單的方式不是去讀取和分析現有AOF文件的內容,,而是直接讀取list鍵在數據庫中的當前值,然后用一條RPUSH list "C" "D" "E" "F" "G"代替前面的6條命令。
- 偽代碼表示如下
? ? AOF后臺重寫
aof_rewrite函數可以創建新的AOF文件,但是這個函數會進行大量的寫入操作,所以調用這個函數的線程被長時間的阻塞,因為服務器使用單線程來處理命令請求;所以如果直接是服務器進程調用AOF_REWRITE函數的話,那么重寫AOF期間,服務器將無法處理客戶端發送來的命令請求;
Redis不希望AOF重寫會造成服務器無法處理請求,所以將AOF重寫程序放到子進程(后臺)里執行。這樣處理的好處是:?
1)子進程進行AOF重寫期間,主進程可以繼續處理命令請求;
2)子進程帶有主進程的數據副本,使用子進程而不是線程,可以避免在鎖的情況下,保證數據的安全性。
還有一個問題,可能重寫的時候又有新的命令過來,造成信息不對等,所以redis設置了一個緩沖區,重寫期間把命令放到重寫緩沖區。
?
? 總結
AOF重寫的目的是為了解決AOF文件體積膨脹的問題,使用更小的體積來保存數據庫狀態,整個重寫過程基本上不影響Redis主進程處理命令請求;
AOF重寫其實是一個有歧義的名字,實際上重寫工作是針對數據庫的當前狀態來進行的,重寫過程中不會讀寫、也不適用原來的AOF文件;
AOF可以由用戶手動觸發,也可以由服務器自動觸發。
?
總結
以上是生活随笔為你收集整理的redis——持久化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构课上笔记10
- 下一篇: 第二次作业 讲解及展示