Redis有兩種持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件)html
RDB持久化方式是在一個特定的間隔保存某個時間點的一個數據快照。linux
AOF(Append only file)持久化方式則會記錄每個服務器收到的寫操做。數據回覆時,這些記錄的操做會逐條執行從而重建出原來的數據。寫操做命令 記錄的格式跟Redis協議一致,以追加的方式進行保存。redis
Redis的持久化是能夠禁用的,兩種方式的持久化是能夠同時存在的,可是當Redis重啓時,AOF文件會被優先用於重建數據。數據庫
RDB就是Snapshot存儲,是默認的持久化方式。按照必定的策略週期性的將數據保存到磁盤。對應產生的數據文件爲dump.rdb,經過配置文件中的save參數來定義快照的週期。Redis支持將當前數據的快照存成一個數據文件實現持久化。而一個持續寫入的數據庫如何生成快照呢。Redis藉助了fork命令的copy on write機制。在生成快照時,將當前進程fork出一個子進程,而後在子進程中循環全部的數據,將數據寫成爲RDB文件。緩存
Client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主線程中保存快照的,因爲redis是用一個主線程來處理全部 client的請求,這種方式會阻塞全部client請求,因此不推薦使用。另外一點須要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步髒數據。若是數據量大的話,並且寫操做比較多,必然會引發大量的磁盤io操做,可能會嚴重影響性能。 安全
Redis的RDB文件不會壞掉,由於其寫操做是在一個新進程中進行的。當生成一個新的RDB文件時,Redis生成的子進程會先將數據寫到一個臨時文件中,而後經過原子性rename系統調用將臨時文件重命名爲RDB文件。這樣在任什麼時候候出現故障,Redis的RDB文件都老是可用的。而且Redis的RDB文件也是Redis主從同步內部實現中的一環bash
第一次Slave向Master同步的實現是:服務器
Slave向Master發出同步請求,Master先dump出rdb文件,而後將rdb文件全量傳輸給slave,而後Master把緩存的命令轉發給Slave,初次同步完成。app
第二次以及之後的同步實現是:工具
Master將變量的快照直接實時依次發送給各個Slave。但無論什麼緣由致使Slave和Master斷開重連都會重複以上兩個步驟的過程。
Redis的主從複製是創建在內存快照的持久化基礎上的,只要有Slave就必定會有內存快照發生。
Redis調用fork(),產生一個子進程。
父進程繼續處理client請求,子進程把內存數據寫到一個臨時的RDB文件。因爲os的寫時複製機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會爲父進程要修改的頁面建立副本,而不是寫共享的頁面。因此子進程的地址空間內的數據是fork時刻整個數據庫的一個快照。
當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,而後子進程退出
RDB文件是一個很簡潔的單文件,它保存了某個時間點的Redis數據,很適合用於作備份。你能夠設定一個時間點對RDB文件進行歸檔,這樣就能在須要的時候很輕易的把數據恢復到不一樣的版本。
RDB很適合用於災備。單文件很方便就能傳輸到遠程的服務器上。
RDB的性能很好,須要進行持久化時,主進程會fork一個子進程出來,而後把持久化的工做交給子進程,本身不會有相關的I/O操做。
比起AOF,在數據量比較大的狀況下,RDB的啓動速度更快。
RDB容易形成數據的丟失。假設每5分鐘保存一次快照,若是Redis由於某些緣由不能正常工做,那麼從上次產生快照到Redis出現問題這段時間的數據就會丟失了。
RDB使用fork()
產生子進程進行數據的持久化,若是數據比較大的話可能就會花費點時間,形成Redis中止服務幾毫秒。若是數據量很大且CPU性能不是很好的時候,中止服務的時間甚至會到1秒。
默認Redis會把快照文件存儲爲當前目錄下一個名爲dump.rdb
的文件。要修改文件的存儲路徑和名稱,能夠經過修改配置文件redis.conf
實現:
# RDB文件名,默認爲dump.rdb。 dbfilename dump.rdb # 文件存放的目錄,AOF文件一樣存放在此目錄下。默認爲當前工做目錄。 dir ./
你能夠配置保存點,使Redis若是在每N秒後數據發生了M次改變就保存快照文件。例以下面這個保存點配置表示每60秒,若是數據發生了1000次以上的變更,Redis就會自動保存快照文件:
save 60 1000
保存點能夠設置多個,Redis的配置文件就默認設置了3個保存點:
# 格式爲:save <seconds> <changes> # 能夠設置多個。 save 900 1 #900秒後至少1個key有變更 save 300 10 #300秒後至少10個key有變更 save 60 10000 #60秒後至少10000個key有變更
若是想禁用快照保存的功能,能夠經過註釋掉全部"save"配置達到,或者在最後一條"save"配置後添加以下的配置:
save ""
默認狀況下,若是Redis在後臺生成快照的時候失敗,那麼就會中止接收數據,目的是讓用戶能知道數據沒有持久化成功。可是若是你有其餘的方式能夠監控到Redis及其持久化的狀態,那麼能夠把這個功能禁止掉。
stop-writes-on-bgsave-error yes
默認Redis會採用LZF
對數據進行壓縮。若是你想節省點CPU的性能,你能夠把壓縮功能禁用掉,可是數據集就會比沒壓縮的時候要打。
rdbcompression yes
從版本5的RDB的開始,一個CRC64
的校驗碼會放在文件的末尾。這樣更能保證文件的完整性,可是在保存或者加載文件時會損失必定的性能(大概10%)。若是想追求更高的性能,能夠把它禁用掉,這樣文件在寫入校驗碼時會用0
替代,加載的時候看到0
就會直接跳過校驗
rdbchecksum yes
Redis提供了兩個命令用於手動生成快照。
SAVE命令會使用同步的方式生成RDB快照文件,這意味着在這個過程當中會阻塞全部其餘客戶端的請求。所以不建議在生產環境使用這個命令,除非由於某種緣由須要去阻止Redis使用子進程進行後臺生成快照(例如調用fork(2)
出錯)。
BGSAVE命令使用後臺的方式保存RDB文件,調用此命令後,會馬上返回OK
返回碼。Redis會產生一個子進程進行處理並馬上恢復對客戶端的服務。在客戶端咱們可使用LASTSAVE命令查看操做是否成功。
127.0.0.1:6379> BGSAVE Background saving started 127.0.0.1:6379> LASTSAVE (integer) 1433936394
配置文件裏禁用了快照生成功能不影響
SAVE
和BGSAVE
命令的效果。
快照並非很可靠。若是服務器忽然Crash了,那麼最新的數據就會丟失。而AOF文件則提供了一種更爲可靠的持久化方式。每當Redis接受到會修改數據集的命令時,就會把命令追加到AOF文件裏,當你重啓Redis時,AOF裏的命令會被從新執行一次,重建數據
redis調用fork ,如今有父子兩個進程
子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令
父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證若是子進程重寫失敗的話並不會出問題
當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。而後父進程把緩存的寫命令也寫入到臨時文件
如今父進程可使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加
比RDB可靠。你能夠制定不一樣的fsync策略:不進行fsync、每秒fsync一次和每次查詢進行fsync。默認是每秒fsync一次。這意味着你最多丟失一秒鐘的數據。
AOF日誌文件是一個純追加的文件。就算服務器忽然Crash,也不會出現日誌的定位或者損壞問題。甚至若是由於某些緣由(例如磁盤滿了)命令只寫了一半到日誌文件裏,咱們也能夠用redis-check-aof
這個工具很簡單的進行修復。
當AOF文件太大時,Redis會自動在後臺進行重寫。重寫很安全,由於重寫是在一個新的文件上進行,同時Redis會繼續往舊的文件追加數據。新文件上會寫入能重建當前數據集的最小操做命令的集合。當新文件重寫完,Redis會把新舊文件進行切換,而後開始把數據寫到新文件上。
AOF把操做命令以簡單易懂的格式一條接一條的保存在文件裏,很容易導出來用於恢復數據。例如咱們不當心用FLUSHALL
命令把全部數據刷掉了,只要文件沒有被重寫,咱們能夠把服務停掉,把最後那條命令刪掉,而後重啓服務,這樣就能把被刷掉的數據恢復回來。
在相同的數據集下,AOF文件的大小通常會比RDB文件大。
在某些fsync策略下,AOF的速度會比RDB慢。一般fsync設置爲每秒一次就能得到比較高的性能,而在禁止fsync的狀況下速度能夠達到RDB的水平。
在過去曾經發現一些很罕見的BUG致使使用AOF重建的數據跟原數據不一致的問題。
把配置項appendonly
設爲yes
:
appendonly yes
# 文件存放目錄,與RDB共用。默認爲當前工做目錄。 dir ./ # 默認文件名爲appendonly.aof appendfilename "appendonly.aof"
你能夠配置Redis調用fsync的頻率,有三個選項:
每當有新命令追加到AOF的時候調用fsync。速度最慢,可是最安全。
每秒fsync一次。速度快(2.4版本跟快照方式速度差很少),安全性不錯(最多丟失1秒的數據)。
從不fsync,交由系統去處理。這個方式速度最快,可是安全性沒有保證
推薦使用每秒fsync一次的方式(默認的方式),由於它速度快,安全性也不錯。相關配置以下:
# appendfsync always appendfsync everysec # appendfsync no
隨着寫操做的不斷增長,AOF文件會愈來愈大。例如你遞增一個計數器100次,那麼最終結果就是數據集裏的計數器的值爲最終的遞增結果,可是AOF文件裏卻會把這100次操做完整的記錄下來。而事實上要恢復這個記錄,只須要1個命令就好了,也就是說AOF文件裏那100條命令其實能夠精簡爲1條。因此Redis支持這樣一個功能:在不中斷服務的狀況下在後臺重建AOF文件。
工做原理以下:
Redis調用fork(),產生一個子進程。
子進程把新的AOF寫到一個臨時文件裏。
主進程持續把新的變更寫到內存裏的buffer,同時也會把這些新的變更寫到舊的AOF裏,這樣即便重寫失敗也能保證數據的安全。
當子進程完成文件的重寫後,主進程會得到一個信號,而後把內存裏的buffer追加到子進程生成的那個新AOF裏。
咱們能夠經過配置設置日誌重寫的條件:
#在日誌重寫時,不進行命令追加操做,而只是將其放在緩衝區裏,避免與命令的追加形成DISK IO上的衝突。 #設置爲yes表示rewrite期間對新寫操做不fsync,暫時存在內存中,等rewrite完成後再寫入,默認爲no,建議yes no-appendfsync-on-rewrite yes # Redis會記住自從上一次重寫後AOF文件的大小(若是自Redis啓動後還沒重寫過,則記住啓動時使用的AOF文件的大小)。 # 若是當前的文件大小比起記住的那個大小超過指定的百分比,則會觸發重寫。 # 同時須要設置一個文件大小最小值,只有大於這個值文件纔會重寫,以防文件很小,可是已經達到百分比的狀況。 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
要禁用自動的日誌重寫功能,咱們能夠把百分比設置爲0:
auto-aof-rewrite-percentage 0
Redis 2.4以上才能夠自動進行日誌重寫,以前的版本須要手動運行BGREWRITEAOF這個命令。
若是由於某些緣由(例如服務器崩潰)AOF文件損壞了,致使Redis加載不了,能夠經過如下方式進行修復:
備份AOF文件。
使用redis-check-aof
命令修復原始的AOF文件:
$ redis-check-aof --fix
可使用diff -u
命令看下兩個文件的差別。
使用修復過的文件重啓Redis服務。
這裏只說Redis >= 2.2版本的方式:
備份一個最新的dump.rdb
的文件,並把備份文件放在一個安全的地方。
運行如下兩條命令:
$ redis-cli config set appendonly yes $ redis-cli config set save ""
確保數據跟切換前一致。
確保數據正確的寫到AOF文件裏。
第二條命令是用來禁用RDB的持久化方式,可是這不是必須的,由於你能夠同時啓用兩種持久化方式。
記得對配置文件
redis.conf
進行編輯啓用AOF,由於命令行方式修改配置在重啓Redis後就會失效。
從上面看出,RDB和AOF操做都是順序IO操做,性能都很高。而同時在經過RDB文件或者AOF日誌進行數據庫恢復的時候,也是順序的讀取數據加載到內存中。因此也不會形成磁盤的隨機讀。
到底選擇什麼呢?下面是來自官方的建議:
一般,若是你要想提供很高的數據保障性,那麼建議你同時使用兩種持久化方式。若是你能夠接受災難帶來的幾分鐘的數據丟失,那麼你能夠僅使用RDB。不少用戶僅使用了AOF,可是咱們建議,既然RDB能夠時不時的給數據作個完整的快照,而且提供更快的重啓,因此最好仍是也使用RDB。
在數據恢復方面:RDB的啓動時間會更短,緣由有兩個
一是RDB文件中每一條數據只有一條記錄,不會像AOF日誌那樣可能有一條數據的屢次操做記錄。因此每條數據只須要寫一次就好了。
另外一個緣由是RDB文件的存儲格式和Redis數據在內存中的編碼格式是一致的,不須要再進行數據編碼工做,因此在CPU消耗上要遠小於AOF日誌的加載。
上面說了RDB快照的持久化,須要注意:在進行快照的時候(save),fork出來進行dump操做的子進程會佔用與父進程同樣的內存,真正的copy-on-write,對性能的影響和內存的耗用都是比較大的。好比機器8G內存,Redis已經使用了6G內存,這時save的話會再生成6G,變成12G,大於系統的8G。這時候會發生交換;要是虛擬內存不夠則會崩潰,致使數據丟失。因此在用redis的時候必定對系統內存作好容量規劃。
目前,一般的設計思路是利用Replication機制來彌補aof、snapshot性能上的不足,達到了數據可持久化。即Master上Snapshot和AOF都不作,來保證Master的讀寫性能,而Slave上則同時開啓Snapshot和AOF來進行持久化,保證數據的安全性。
經過上面的理論對snapshot和aof有了必定的理解,下面開始進行一些測試
save 900 1 save 300 10 save 60 10000 rdbcompression no rdbchecksum no dbfilename redis.rdb dir /home/backup/redis appendonly no
[root@localhost redis]# ./src/redis-cli 127.0.0.1:6379> keys * 1) "a" 127.0.0.1:6379> set b 2 OK 127.0.0.1:6379> set c 3 OK 127.0.0.1:6379> set d 4 OK 127.0.0.1:6379> keys * 1) "c" 2) "a" 3) "aa" 4) "b" 5) "d" 127.0.0.1:6379> save OK #保存,進行持久化,每執行一次save,會在日至裏面記錄一條:" * DB saved on disk " 127.0.0.1:6379> lpush aa 1 (integer) 1 127.0.0.1:6379> lpush aa 2 (integer) 2
持久化驗證,重啓redis
127.0.0.1:6379> keys * 1) "c" 2) "a" 3) "aa" 4) "b" 5) "d"
lpush 操做在 save以後,可是重啓以後仍然有這個數據
什麼緣由呢,咱們能夠查看一下日誌
6720:signal-handler (1453738444) Received SIGTERM scheduling shutdown... 6720:M 26 Jan 00:14:04.896 # User requested shutdown... 6720:M 26 Jan 00:14:04.896 * Saving the final RDB snapshot before exiting. 6720:M 26 Jan 00:14:04.932 * DB saved on disk 6720:M 26 Jan 00:14:04.932 * Removing the pid file. 6720:M 26 Jan 00:14:04.932 # Redis is now ready to exit, bye bye...
從日誌裏面能夠看到,正常關閉redis,在關閉前執行save命令。 用kill的效果和上面同樣,屬於正常關閉
那異常關閉呢?當以kill -9 的形式發送信號
127.0.0.1:6379> set ss 1 Could not connect to Redis at 127.0.0.1:6379: Connection refused not connected> get ss Could not connect to Redis at 127.0.0.1:6379: Connection refused not connected> get ss (nil)
經過測試,開啓RDB持久化,在知足save條件、手動save、正常關閉的時候數據都會被持久化;而異常關閉終止的時候數據會丟失
#save 900 1 #save 300 10 #save 60 10000 rdbcompression no rdbchecksum no dbfilename redis.rdb dir ./ appendonly no
操做
redis 127.0.0.1:6379> keys * (empty list or set) redis 127.0.0.1:6379> set name test OK redis 127.0.0.1:6379> save OK redis 127.0.0.1:6379> set aa 1 OK redis 127.0.0.1:6379> #重啓redis redis 127.0.0.1:6379> keys * #發現剛纔沒有被保存的key丟失了 1) "name"
從上面的結果看出,關閉持久化,只有在手動save的時候數據都會被持久化,正常關閉的時候數據丟失。若是從一開始到關閉寫入數據的期間沒有手動save,則數據所有丟失,既然能手動save間接的說明了快照一直都存在,因此不能說是禁止snapshot,應該是禁止自動snapshot功能。
appendonly yes appendfilename redis.aof # appendfsync always appendfsync everysec # appendfsync no no-appendfsync-on-rewrite no auto-aof-rewrite-min-size 64mb
操做
redis 127.0.0.1:6379> keys * 1) "name" #修改開啓AOF參數,重啓數據庫: redis 127.0.0.1:6379> keys * (empty list or set) redis 127.0.0.1:6379> #數據庫裏面沒有記錄 #查看日誌: #* DB loaded from append only file: 0.000 seconds #發現是從0字節的aof文件裏面同步數據,爲何不一樣步rdb的數據?原來redis代碼裏面寫好了優先級,AOF>RDB
查看源代碼 redis.c grep 'DB loaded from' ./ -R
void loadDataFromDisk(void) { long long start = ustime(); if (server.aof_state == REDIS_AOF_ON) { if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK) redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000); } else { if (rdbLoad(server.rdb_filename) == REDIS_OK) { redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds", (float)(ustime()-start)/1000000); } else if (errno != ENOENT) { redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno)); exit(1); } } }
這裏須要注意的是:當中途開啓AOF,重啓讓生效的時候,不能第2次正常重啓了
由於第一次重啓讓aof生效的時候,啓動redis已經讀取這個文件了,致使此時的redis數據爲空的(優先級)。第二次重啓,則會把這個空的數據save到RDB文件,這樣致使RDB原有的數據被替換,致使數據丟失。因此必定要當心,爲了不悲劇的發生,當要重啓redis的時候最好都備份下RDB文件。
redis 127.0.0.1:6379> keys * (empty list or set) redis 127.0.0.1:6379> set name tt OK redis 127.0.0.1:6379> save OK #開啓aof參數 #第一次重啓 redis 127.0.0.1:6379> keys * #如上面所說的優先級緣由:aof > rdb,結果爲空 (empty list or set) #第2次正常重啓,把上面空的結果save到了RDB,數據丟失。此時的db是空的,日誌記錄 "* DB saved on disk" redis 127.0.0.1:6379> keys * (empty list or set) #數據已經被初始化了,數據丟失
這裏就有一個問題,好比在用redis的時候,剛開始只開啓RDB的持久方式,AOF沒有開啓,在跑一段時間以後想開啓AOF,那如何把RDB的數據直接寫到AOF文件呢?有2種方法
a、在開啓AOF以前,先執行bgrewriteaof,再重啓
redis 127.0.0.1:6379> keys * #查看是否有數據 (empty list or set) redis 127.0.0.1:6379> set name ttd OK redis 127.0.0.1:6379> keys * 1) "name" redis 127.0.0.1:6379> bgsave #保存數據 Background saving started redis 127.0.0.1:6379> keys * 1) "name" #只有一個RDB文件,沒有AOF文件 redis 127.0.0.1:6379> bgrewriteaof #執行合併重寫功能,生成AOF文件 Background append only file rewriting started #這時候去打開redis.conf 文件中的aof參數(appendonly yes),重啓生效。 #日誌裏面出現:* DB loaded from append only file: 0.000 seconds redis 127.0.0.1:6379> keys * #數據還在 1) "name" #查看文件 [root@localhost data]# od -c redis.aof 0000000 * 2 \r \n $ 6 \r \n S E L E C T \r \n 0000020 $ 1 \r \n 0 \r \n * 3 \r \n $ 3 \r \n S 0000040 E T \r \n $ 4 \r \n n a m e \r \n $ 4 0000060 \r \n j a c k \r \n 0000070
b、利用CONFIG GET/SET 的方法動態修改配置文件
redis 127.0.0.1:6379> BGSAVE Background saving started #此時,只有rdb文件 #動態修改參數,把aof功能開啓:appendonly yes redis 127.0.0.1:6379> CONFIG SET appendonly yes #動態修改參數 OK redis 127.0.0.1:6379> CONFIG GET append* 1) "appendonly" 2) "yes" 3) "appendfsync" 4) "everysec" redis 127.0.0.1:6379> #aof文件已經生成,而且有數據(同步rdb) #日誌裏面的信息:* Background append only file rewriting started by pid 3165 #由於參數是動態修改的,在重啓以後會失效,因此在維護的時候修改redis.conf文件的參數便可
從上面的結果看出,redis重啓載入數據的時候,讀取aof的文件要先於rdb文件,因此儘可能一開始開啓aof選項,不要在中途開啓。
經過日誌能夠很清楚的知道redis經過那個文件來取數據的:
RDB: * DB loaded from disk: 0.000 seconds AOF: * DB loaded from append only file: 0.000 seconds
保存數據則是
RDB:* DB saved on disk AOF: * Calling fsync() on the AOF file
save 900 1 save 300 10 save 60 10000 appendonly yes appendfilename zhoujy.aof # appendfsync always appendfsync everysec # appendfsync no no-appendfsync-on-rewrite no auto-aof-rewrite-min-size 64mb
經過上面的這些測試,已經說明RDB和AOF他們的操做方式,以及如重啓時的載入,重啓時將按照如下優先級恢復數據到內存:
若是隻配置AOF,重啓時加載AOF文件恢復數據
若是同時 配置了RBD和AOF,啓動是隻加載AOF文件恢復數據
若是隻配置RDB,啓動時候加載dump文件恢復數據
4、Redis數據備份
備份很簡單,只須要把RDB,AOF的文件複製備份起來就能夠了
#redisA: A上生成測試數據 redis 127.0.0.1:6379> set name test 7.0.0.1:6379> set age 17 OK redis 127.0.0.1:6379> keys * 1) "age" redis 127.0.0.1:6379> bgsave Background saving started #redisB: B上沒有數據 redis 127.0.0.1:6380> keys * (empty list or set) #複製A的文件到B(rdb和aof文件) cp redis/* redis2/ #修改權限 chown -R redis.redis * #重啓B 還原 redis 127.0.0.1:6380> keys * 1) "sex"
參考
http://redis.io/topics/persistencehttp://www.cnblogs.com/zhoujinyi/archive/2013/05/26/3098508.htmlhttp://heylinux.com/archives/1932.htmlhttp://database.51cto.com/art/201203/322144.htm