1. 程式人生 > 實用技巧 >NOIP普及 火星人(STL做法+非STL做法)

NOIP普及 火星人(STL做法+非STL做法)

redis 持久化

Redis 支援兩種持久化方式, RDB方式和AOF方式, 前者會根據指定的規則“定時”將記憶體中的資料儲存在硬碟上,後者在每次執行命令後將命令本身記錄下來。二者可單獨使用一種,但多數情況是二者結合使用

tags: #redis #redis-RDB #redis-AOF

RDB 方式

RDB 方式通過快照所完成,當符合一定條件時, redis 會自動將記憶體的所有資料生成一份副本並存儲在硬碟上,這個過程即為快照

redis 會在以下幾種情況進行資料快照:

  • 根據配置規則進行自動快照
  • 使用者執行SAVE或者BGSAVE 命令
  • 執行FLASHALL命令
  • 執行復制時
根據配置規則進行自動快照

redis 安裝目錄的配置中預置了3個條件:

save 900 1
save 300 10
save 60 10000

當 redis 符合快照條件時會自動執行快照操作。 快照條件的配置由兩個引數構成: 時間M 和改動的鍵的個數N。每條快照條件佔一行且以 save 開頭,同時可存在多個條件,條件之間是 ‘或’ 的關係, 就以上的例子而言, save 900 1 意為:在15分鐘(即900秒)內有一個或一個以上的鍵被更改則進行快照,同理, save 300 10 表示在300秒內至少有10個鍵被修改則進行快照

使用者執行SAVE或者BGSAVE 命令

當服務重啟、手動遷移及備份時也會需要到手動執行快照操作,redis 提供兩個命令來完成這一任務。

SAVE 命令

當執行SAVE命令時,redis 同步的進行快照操作,在快照執行的過程中會阻塞所有來自客戶端的請求,當資料庫中的資料較多時, 這一過程會導致 redis 較長時間不響應。生產環境儘量避免使用。

BGSAVE 命令

需要手動執行快照時推薦使用BGSAVE命令,其可在後臺非同步進行快照操作,快照的同時伺服器還可繼續響應來自客戶端的請求,執行完此命令,redis 會返回OK表示開始執行快照操作,若想知道快照是否完成。 可通過 LASTSAVE 命令獲取最近一次成功執行快照的時間,返回結果是一個 unix 時間戳。

非同步快照的具體過程參考快照原理

執行FLASHALL命令

執行FLASHALL時, redis 會清除資料庫中的所有的資料 。 需要注意的是, 不論清空資料庫的過程是否觸發了自動快照條件,只要自動快照條件不為空, redis 就會執行一次快照操作。

例如: 當定義的快照條件為當1秒內修改10000個鍵時進行自動快照,而資料庫中只有一個鍵時,執行FLASHALL也會觸發快照,即便這一過程只有一個鍵被修改了。
當沒有定義自動快照條件的時候,執行FLASHALL則不會進行快照

執行復制時

當設定了主從模式時,redis 會在複製初始化時,進行自動快照。 使用複製操作時,即便沒有定義自動快照條件,且沒有手動執行過快照, 也會生成RDB快照檔案

快照原理

redis 預設會將快照檔案儲存在 redis 當前程序的工作目錄中的 dump.rdb 檔案中。 可以通過配置 dir 和 dbfile 兩個引數分別指定快照檔案的儲存路徑和檔名。快照過程如下 :

  1. redis 使用 fork 函式複製一份當前程序(父程序)的副本(子程序)
  2. 父程序繼續接受並處理客戶端發來的命令, 而子程序開始將記憶體中的資料寫入硬碟中的臨時檔案
  3. 當子程序寫入完所有資料後會用該臨時檔案替換舊的RDB檔案,至此一次快照操作完成。

在執行 fork 的時候, 作業系統(類 unix 作業系統)會使用寫時複製(copy-on-write)策略,即 fork 函式發生的那一刻, 父子程序共享同一記憶體資料, 當父程序要改某一片資料時(如執行一個寫命令), 作業系統會將該片資料複製一份,以保證子程序資料不受影響,所以新的RDB檔案儲存的是執行 fork 那一刻的資料。
寫時複製策略也保證了在 fork 的時刻雖然看上去生成了兩份記憶體副本,但實際上記憶體的佔用量並不會增加一倍。這就意味著當系統記憶體只有2GB,而 Redis 資料庫 的記憶體有1.5 GB時,執行fork後記憶體使用量並不會增加到3 GB (超出實體記憶體)。為此需要確保Linux系統允許應用程式申請超過可用記憶體(實體記憶體和交換分割槽)的 空間,方法是在 /etc/sysctl.conf 檔案加入 vm.overcommit_memory = 1,然後重啟系統或者執行 sysctl vm.overcommit_ memory=1 確保設定生效。
另外需要注意的是,當進行快照的過程中,如果寫入操作較多,造成fork前後資料差異較大,是會使得記憶體使用量顯著超過實際資料大小的,因為記憶體中不僅儲存了當前的資料庫資料,而且還儲存著fork時刻的記憶體資料。進行記憶體用量估算時很容易忽略這一問題,造成記憶體用量超限。

通過上述過程可以發現Redis在進行快照的過程中不會修改RDB檔案,只有快照結束後才會將舊的檔案替換成新的,也就是說任何時候RDB檔案都是完整的。這使得我們可 以通過定時備份RDB檔案來實現Redis 資料庫備份。RDB檔案是經過壓縮(可以配置rdbcompression引數以禁用壓縮節省CPU佔用)的二進位制格式,所以佔用的空間會小於記憶體中的資料大小,更加利於傳輸。

Redis啟動後會讀取RDB快照檔案,將資料從硬碟載入到記憶體。根據資料量大小與結構和伺服器效能不同,這個時間也不同。通常將一個記錄1000萬個字串型別鍵、大小為 1 GB的快照檔案載入到記憶體中需要花費20~30秒。

通過RDB方式實現持久化,一旦Redis異常退出,就會丟失最後一次快照以後更改的所有資料。這就需要開發者根據具體的應用場合,通過組合設定自動快照條件的方式來將可能發生的資料損失控制在能夠接受的範圍。例如,使用Redis儲存快取資料時,丟失最近幾秒的資料或者丟失最近更新的幾十個鍵並不會有很大的影響。如果資料相對重要,希望將損失降到最小,則可以使用AOF方式進行持久化。

AOF 方式

當使用 redis 儲存非零時資料時, 一般需要開啟AOF持久化來降低程序中止導致的資料丟失。 AOF可將 redis 執行的每一條命令追加到硬碟檔案中,這一過程顯然會降低 redis 到效能,但是大部分情況這個影響是可接受的。 另, 使用較快的硬碟可提升AOF的效能。

開啟AOF

預設情況下 redis 沒有開啟AOF(append only file) 方式的持久化,可以通過 appendonly 引數啟用:appendonly yes

開啟AOF持久化後每執行一條會更改 redis 中資料的命令, redis 就會將該命令寫入磁碟中的AOF檔案。 AOF檔案儲存位置和RDB檔案的位置相同, 都是通過 dir 引數設定的, 預設的檔名是 appendonly.aof 可以通過 appendfilename 引數修改:
appendfilename appendonly.aof

在啟動 redis 時會逐個執行AOF檔案中的命令來將硬碟中的資料載入到記憶體中,載入的速度相較RDB會慢一些

AOF 的實現

AOF 檔案以純文字的形式記錄另 redis 執行的寫命令,例如在開啟AOF持久化的情況下執行如下4個命令:

set foo 1
set foo 2
set foo 3
get foo

redis 會將前三條命令寫入AOF檔案。被寫入的AOF檔案的內容是 redis 客戶端向 redis 傳送原始通訊協議的內容

有一個問題是前兩條命令其實是冗餘的,因為這兩條命令執行結果會被第三條命令覆蓋,隨著執行命令越來越多,AOF檔案的大小也會越來越大,即使記憶體中的資料可能並沒有多少。 redis 可以自動優化AOF檔案, 舉上個例子來說,就是將前兩條命令刪掉只保留第三條。 *reids 達到一定條件會自動重寫AOF檔案,這個條件可在配置檔案中設定: *

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

auto-aof-rewrite-percentage 引數的意義是當目前AOF檔案大小超過上一次重寫時AOF檔案大小的百分之多少時會再次重寫,若從未重寫過,則以啟動時的AOF檔案大小為依據。

auto-aof-rewrite-min-size 引數限制了允許重寫的最小AOF檔案大小,通常在AOF檔案很小的情況下即使其中有很多冗餘的命令我們也並不太關心。 除了讓 redis 自動重寫之外,我們還可以主動使用·BGREWRITEAOF命令執行AOF重寫

同步硬碟資料

雖然每次執行更改資料庫內容的操作時,AOF都會將命令記錄在AOF檔案中, 但事實上,由於作業系統但快取機制,資料並沒有真正寫入硬碟,而是進入了系統但硬碟快取。在預設情況下系統每30秒執行一次同步操作,以便將硬碟快取中的內容真正的寫入硬碟,在這30秒的過程中如果系統異常退出則會導致硬碟快取中的資料丟失,一般來講,啟用AOF的應用都無法容忍這樣的損失, 這就需要 redis 在寫入AOF檔案後主動要求系統將快取同步到硬碟中, 在 redis 中我們可以通過 appendfsync 引數設定同步的時機: ```

# appendfsync always 最安全也是最慢的方式
appendfsync everysec # 即兼顧效能又保證了安全
# appendfsync no # 最快但最不安全

預設情況下, redis 採用 everysec 規則,即每秒執行一次同步操作。 always 表示每次執行寫入都會執行同步。no 表示不主動進行同步操作,而是完全交由系統來做(即每30秒一次),一般情況下使用預設值 everysec 就夠了

redis 允許同時開啟AOF 和 RDB 既保證了資料安全又使得備份等操作十分容易。 此時重啟 redis 後, redis 會使用 AOF 檔案來恢復資料, 因為AOF方式可能丟失的資料更少