redis持久化

Redis持久化簡介:

redis是一個支持持久化的內存數據庫,也就是說redis需要經常將內存中的數據同步到磁盤來保證持久化。redis支持兩種持久化方式,一種是 Snapshotting(快照)也是默認方式,另一種是Append-only file(縮寫aof)的方式。

下面分別介紹

Snapshotting
快照是默認的持久化方式。這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。可以通過配置設置自動做快照持久 化的方式。我們可以配置redis在n秒內如果超過m個key被修改就自動做快照,下面是默認的快照保存配置

save 900 1 #900秒內如果超過1個key被修改,則發起快照保存
save 300 10 #300秒內容如超過10個key被修改,則發起快照保存
save 60 10000

下面介紹詳細的快照保存過程

1.redis調用fork,現在有了子進程和父進程。

2. 父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。由於os的寫時複製機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會爲父進程要修改的頁面創建副本,而不是寫共享的頁面。所以子進程的地址空間內的數 據是fork時刻整個數據庫的一個快照。

3.當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,然後子進程退出。

client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由於redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。另一點需要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步髒數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。

另外由於快照方式是在一定間隔時間做一次的,所以如果redis意外down掉的話,就會丟失最後一次快照後的所有修改。如果應用要求不能丟失任何修改的話,可以採用aof持久化方式。下面介紹

Append-only file

aof 比快照方式有更好的持久化性,是由於在使用aof持久化方式時,redis會將每一個收到的寫命令都通過write函數追加到文件中(默認是 appendonly.aof)。當redis重啓時會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。當然由於os會在內核中緩存 write做的修改,所以可能不是立即寫到磁盤上。這樣aof方式的持久化也還是有可能會丟失部分修改。不過我們可以通過配置文件告訴redis我們想要 通過fsync函數強制os寫入到磁盤的時機。有三種方式如下(默認是:每秒fsync一次)

appendonly yes //啓用aof持久化方式
# appendfsync always //每次收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦
# appendfsync no //完全依賴os,性能最好,持久化沒保證

aof 的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調用incr test命令100次,文件中必須保存全部的100條命令,其實有99條都是多餘的。因爲要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。具體過程如下

1. redis調用fork ,現在有父子兩個進程
2. 子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令
3.父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話並不會出問題。
4.當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。然後父進程把緩存的寫命令也寫入到臨時文件。
5.現在父進程可以使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。

需要注意到是重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。

redis啓動裝載:

AOF優先於RDB
RDB性能優於AOF,因爲裏面沒有重複
Redis一次性將數據加載到內存中,一次性預熱

 

一、驗證redis的快照功能

1. 修改redis配置文件
--下面redis.conf配置文件參數值都默認的,可以根據需要修改
[root@rac1 redis-2.6.8]# vi /etc/redis_master.conf
# 快照保存規則,如
save 900 1 #900秒內如果超過1個key被修改,則發起快照保存
save 300 10 #300秒內容如超過10個key被修改,則發起快照保存
save 60 10000

# 最多使用數據庫的個數
databases 16

# 快照文件的名字
dbfilename dump.rdb

# 快照存放的目錄
dir /opt/redis-2.6.8

# 數據文件是否要壓縮。(爲了方便查看數據文件內容,設置成no)
rdbcompression no

# redis操作的口令
# requirepass foobared

# Append Only File 功能被關閉(注意:爲了更好測試快照功能,此功能最好關閉)
appendonly no

# daemonize:指定Redis是否後臺運行

daemonize yes

 

2. 啓動redis
[root@rac1 ~]# redis-server /etc/redis_master.conf > redis_master.log 2>&1 &
[1] 5009

 

3. 命令測試快照保存
[root@rac1 redis-2.6.8]# redis-cli
redis 127.0.0.1:6379> keys *
(empty list or set)
redis 127.0.0.1:6379> set aaa 1
OK
redis 127.0.0.1:6379> get aaa
"1"
redis 127.0.0.1:6379> set bbb 2
OK
redis 127.0.0.1:6379> set ccc 3
OK
redis 127.0.0.1:6379> save
OK
redis 127.0.0.1:6379> set ddd 4
OK
redis 127.0.0.1:6379> keys *
1) "aaa"
2) "bbb"
3) "ccc"
4) "ddd"
redis 127.0.0.1:6379> exit

--kill掉redis進程
[root@rac1 redis-2.6.8]# kill -9 5009
[root@rac1 redis-2.6.8]#
[1]+ 已殺死 redis-server /etc/redis_master.conf > redis_master.log 2>&1


--查看redis_master.log日誌
[30640] 18 Mar 12:06:07.037 * DB saved on disk #save命令後數據保存到磁盤
[30640] 18 Mar 12:06:07.043 * RDB: 0 MB of memory used by copy-on-write
[30637] 18 Mar 12:06:07.063 * Background saving terminated with success
[30637] 18 Mar 12:06:07.069 * Synchronization with slave succeeded
[30637] 18 Mar 12:07:23.806 * DB saved on disk

4. 查看數據文件內容
[root@rac1 redis-2.6.8]# ll
-rw-rw-r-- 1 root root 14692 01-11 00:15 00-RELEASENOTES
-rw-rw-r-- 1 root root 52 01-11 00:15 BUGS
-rw-rw-r-- 1 root root 1440 01-11 00:15 CONTRIBUTING
-rw-rw-r-- 1 root root 1487 01-11 00:15 COPYING
drwxrwxr-x 6 root root 4096 03-12 16:07 deps
-rw-r--r-- 1 root root 41 03-18 11:59 dump.rdb
-rw-rw-r-- 1 root root 11 01-11 00:15 INSTALL
-rw-rw-r-- 1 root root 151 01-11 00:15 Makefile
-rw-rw-r-- 1 root root 4038 01-11 00:15 MANIFESTO
-rw-rw-r-- 1 root root 4059 01-11 00:15 README
-rw-rw-r-- 1 root root 23259 03-18 11:48 redis.conf
-rwxrwxr-x 1 root root 160 01-11 00:15 runtest
-rw-rw-r-- 1 root root 5754 01-11 00:15 sentinel.conf
drwxrwxr-x 2 root root 4096 03-12 16:11 src
drwxrwxr-x 8 root root 4096 01-11 00:15 tests
drwxrwxr-x 2 root root 4096 01-11 00:15 utils

 

--再次重啓redis,加載快照,發現未保存的ddd丟失
[root@rac1 redis-2.6.8]# redis-server /etc/redis_master.conf > redis_master.log 2>&1 &
[1] 7113
[root@rac1 redis-2.6.8]# redis-cli
redis 127.0.0.1:6379> keys *
1) "aaa"
2) "bbb"
3) "ccc"

[root@rac1 redis-2.6.8]# cat dump.rdb
REDIS0006taaaàbbbàcccà?l
?O¢w?[root@rac1 redis-2.6.8]#


二、驗證redis的AOF功能
和快照功能一樣,實現AOF功能只需要修改配置參數


1. 修改redis.conf配置文件
--主要修改appendonly
[root@rac1 redis-2.6.8]# vi /etc/redis_master.conf
appendonly yes

 

2. 重啓redis服務
[root@rac1 redis-2.6.8]# redis-cli shutdown
[16739] 18 Mar 13:49:18.232 # User requested shutdown...
[16739] 18 Mar 13:49:18.234 * Saving the final RDB snapshot before exiting.
[16739] 18 Mar 13:49:18.245 * DB saved on disk
[16739] 18 Mar 13:49:18.245 # Redis is now ready to exit, bye bye...
[1]+ Done redis-server /etc/redis_master.conf


[root@rac1 redis-2.6.8]# redis-server /etc/redis_master.conf &

[root@rac1 redis-2.6.8]# ll
-rw-r--r-- 1 root root 0 03-18 13:54 appendonly.aof

 

3. 命令測試
[root@rac1 redis-2.6.8]# redis-cli
redis 127.0.0.1:6379> keys *
(empty list or set)
redis 127.0.0.1:6379> set eee 1
OK
redis 127.0.0.1:6379> set xxx 2
OK
redis 127.0.0.1:6379> get eee
"1"
redis 127.0.0.1:6379> get xxx
"2"
redis 127.0.0.1:6379> keys *
1) "xxx"
2) "eee"

 

--查看日誌文件
[root@rac1 redis-2.6.8]# cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$3
eee
$1
1
*3
$3
set
$3
xxx
$1
2

                                                                                                              *********************引用*********************************

兩種持久化方案:RDB和AOF
RDB方式按照一定的時間間隔對數據集創建基於時間點的快照。
AOF方式記錄Server收到的寫操作到日誌文件,在Server重啓時通過回放這些寫操作來重建數據集。該方式類似於MySQL中基於語句格式的binlog。當日志變大時Redis可在後臺重寫日誌。
若僅期望數據在Server運行期間存在則可禁用兩種持久化方案。在同一Redis實例中同時開啓AOF和RDB方式的數據持久化方案也是可以的。該情況下Redis重啓時AOF文件將用於重建原始數據集,因爲叫RDB方式而言,AOF方式能最大限度的保證數據完整性。

兩鍾方案各自的優缺點
RDB優點
RDB是Redis數據集的基於時間點的緊湊的副本,非常適合於備份場景。比如每個小時對RDB文件做一次小的歸檔,每天對RDB文件做一次大的歸檔,每月對RDB文件做一次更大的歸檔。這樣可以在必要的時刻選擇不同的備份版本進行數據恢復。
由於是一個緊湊的文件,易於傳輸到遠程數據中心或Amazon S3,因此RDB非常適合於災難恢復。
RDB方式的開銷較低,在該種方式下Redis父進程所要做的僅是開闢一個子進程來做剩下的事情。
與AOF相比RDB在數據集較大時能夠以更快的速度恢復。

RDB缺點
若需在Redis停止工作時(例如意外斷電)儘可能保證數據不丟失,那麼RDB不是最好的方案。例如,通常會每隔5分鐘或者更長的時間來創建一次快照,如若Redis沒有被正確的關閉就可能丟失最近幾分鐘的數據。
RDB方式需經常調用fork()函數以開闢子進程來實現持久化。在數據集較大、CPU性能不夠強悍時fork()調用可能很耗時從而會導致Redis在幾毫秒甚至一秒中的時間內不能服務clients。AOF也需要調用fork()但卻可以在不影響數據持久性的條件下調整重寫logs的頻率。

AOF優點
使用AOF方式時Redis持久化更可靠:有三種不同的fsync策略供選擇:no fsync at all、fsync every second、 fsync at every query。默認爲fsync every second此時的寫性能仍然很好,且最壞的情況下可能丟失一秒鐘的寫操作。
AOF日誌是append only方式產生的日誌,因此不存在隨機訪問問題以及意外斷電時造成的損毀問題。即使出於某種原因(如磁盤滿)日誌以一個寫了一半的命令結尾,仍可以使用redis-check-aof工具快速進行修復。
當AOF日誌逐漸變大後,Redis可在後臺自動的重寫AOF日誌。當Redis在繼續追加舊的AOF日誌文件時重寫日誌是完全安全的。Redis利用可以重建當前數據集的最少的命令產生一個全新的日誌文件,一旦新的日誌文件創建完成Redis開始向新的日誌文件追加日誌。
AOF日誌的格式易於理解易於解析。這在某些場景非常有用。比如,不下心使用FLUSHALL命令清空了所有的數據,同時AOF日誌沒有發生重寫操作,那麼就可以簡單的通過停止Redis Server移除日誌中的最後一條FLUSHALL命令重啓Redis Server來恢復數據。

AOF缺點
同樣的數據集AOF文件要比RDB文件大很多。
根據使用的fsync方式不同AOF可能比RDB慢很多。在使用no fsync at all時AOF的性能基本與RDB持平,在使用fsync every second時性能有所下降但仍然較高,在使用 fsync at every query時性能較低。然而RDB方式卻能在高負載的情況下保證延遲儘可能小。
一些特定的命令可能存在bug從而導致重載AOF日誌時不能重建出完全一樣的數據集。這樣的bugs非常非常罕見,已經通過測試套件做了充分的測試。這種類型的bugs對於RDB來說幾乎是不可能的。說的更清晰一點:Redis AOF增量的更新既存的狀態而RDB快照每次都重新創建,從概念上講RDB方式更加健壯。然而,需要注意兩點:每次AOF日誌被Redis重寫的時候日誌由包含數據集的實際數據重新生成,與追加AOF文件的方式相比該方式能有效減少bugs出現的概率;現實的應用場景中還未收到過任何用戶關於AOF損毀的報告。

如何選擇持久化方式?
取決於具體的應用場景,通常,兩種方式可同時使用。若比較關心數據但仍能忍受幾分鐘的數據丟失,那麼可以簡單的使用RDB方式。有許多用戶只使用AOF方式,不建議這種做法,一方面以一定時間間隔創建RDB快照是創建數據備份並快速恢復數據的極好的辦法,一方面可以避免AOF方式可能存在的bugs。出於上述原因,將來可能將AOF和RDB方式合二爲一。

RDB持久化設置
默認情況下Redis在磁盤上創建二進制格式的命名爲dump.rdb的數據快照。可以通過配置文件配置每隔N秒且數據集上至少有M個變化時創建快照、是否對數據進行壓縮、快照名稱、存放快照的工作目錄。redis 2.4.10的默認配置如下:

#900秒後且至少1個key發生變化時創建快照
save 900 1
#300秒後且至少10個key發生變化時創建快照
save 300 10
#60秒後且至少10000個key發生變化時創建快照
save 60 10000
#可通過註釋所有save開頭的行來禁用RDB持久化
#創建快照時對數據進行壓縮
rdbcompression yes
#快照名稱
dbfilename dump.rdb
#存放快照的目錄(AOF文件也會被存放在此目錄)
dir /var/lib/redis/

關於配置參數的詳細信息可參閱redis.conf中的說明。

除了通過配置文件進行設置外也可以通過手工執行命令來創建快照
SAVE命令執行一個同步操作,以RDB文件的方式保存實例中所有數據的快照。一般不在生產環境直接使用SAVE 命令,因爲會阻塞所有的客戶端的請求,可以使用BGSAVE命令代替。BGSAVE後臺創建數據快照。命名執行結果的狀態碼會立即返回。Redis開闢一個子進程,父進程繼續相應客戶端請求,子進程保存DB到磁盤後退出。客戶端可通過執行LASTSAVE命令檢查操作是否成功。

創建RDB快照的工作流程
Redis需dump數據集到磁盤時會執行下列過程:
Redis forks一個子進程;
子進程寫數據集到臨時的RDB文件;
子進程寫完新的RDB文件後替換舊的RDB文件。
該方式使Redis可以利用copy-on-write機制的好處

AOF持久化設置
利用快照的持久化方式不是非常可靠,當運行Redis的計算機停止工作、意外掉電、意外殺掉了Redis進程那麼最近寫入Redis的數據將會丟。對於某些應用這或許不成問題,但對於持久化要求非常高的應用場景快照方式不是理想的選擇。AOF文件是一個替代方案,用以最大限度的持久化數據。同樣,可以通過配置文件來開閉AOF:

#關閉AOF
appendonly no
#打開AOF
appendonly yes

當設置appendonly爲yes後,每次Redis接收到的改變數據集的命令都會被追加到AOF文件。重啓Redis後會重放AOF文件來重建數據。 

還可以通過配置文件配置AOF文件名、調用fsync的頻率、調用fsync的行爲、重寫AOF的條件。redis 2.4.10的默認配置如下:

#默認AOF文件名
appendfilename appendonly.aof
#每秒調用一次fsync刷新數據到磁盤
appendfsync everysec
#當進程中BGSAVE或BGREWRITEAOF命令正在執行時不阻止主進程中的fsync()調用(默認爲no,當存在延遲問題時需調整爲yes)
no-appendfsync-on-rewrite no
#當AOF增長率爲100%且達到了64mb時開始自動重寫AOF
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

各參數含義可參閱redis.conf中詳細說明。

幾點說明
日誌重寫
隨着Redis接收到的命令的增加AOF文件會變得越來越大。Redis支持日誌重寫特性,可以在不影響響應客戶端的前提下在後臺重構AOF文件。當在Redis中執行BGREWRITEAOF後Redis將使用構建數據集所需的最少的命令來重構日誌文件。Redis2.2中需要經常手動運行BGREWRITEAOF,Redis2.2開始支持自動觸發日誌重寫。

日誌重寫同樣使用copy-on-write機制,流程大致如下:
Redis開闢一個子進程;
子進程在臨時文件中寫新的AOF文件;
父進程將所有新的更改緩存在memory中(同時新更改被寫入舊的AOF,這樣即使重寫操作失敗了也是安全的);
在子進程重寫好臨時AOF後父進程收到一個信號並追加memory中緩衝的更改到子進程產生的臨時文件的末尾;
Redis進行文件重命名用新的文件替換舊的文件並開始追加新的數據到新文件。

fsync調用模式

該模式決定了Redis刷新數據到磁盤的頻率,有三個可選項:
no fsync at all 全由操作系統決定刷數據的時機。最快但最不安全。
fsync every second 每秒一次刷新。足夠快,最多可丟失一秒的數據。
fsync at every query 每次記錄一條新的命令到AOF便刷一次數據到磁盤。最慢但最安全。
默認策略(也是默認策略)爲fsync every second

AOF損壞時的對策
若在寫AOF文件時Server崩潰則可能導致AOF文件損壞而不能被Redis載入。可通過如下步驟修復:
創建一個AOF文件的備份;
使用redis-check-aof工具修復原始的AOF文件;
$ redis-check-aof --fix
使用diff -u 檢查備份文件和修復後文件的異同(可選步驟);
使用修復後的AOF文件重啓Redis。

如何由RDB持久化轉換到AOF持久化?
Redis >=2.2時
創建最近的RDB文件的備份;
將備份保存在安全的位置;
發起如下命令;
$redis-cli config set appendonly yes
$redis-cli config set save ""(可選,若不執行RDB和AOF方式將並存)
確認數據庫包含相同的keys;
確認write操作被正確追加到了AOF文件。
注意事項:記得修改redis.conf中對應的配置以免Redis Server重啓後通過命令進行的配置更新丟失而重新使用舊的配置文件中配置。

Redis2.0時
創建最近的RDB文件的備份;
將備份存放在安全的位置;
停止數據庫上的所有寫操作;
發起 redis-cli bgrewriteaof命令創建AOF文件;
當AOF文件生成後停止Redis Server;
編輯redis.conf開啓AOF持久化;
重啓Redis Server;
確認數據庫包含相同的keys;
確認write操作被正確追加到了AOF文件。

AOF與RDB之間的相互影響
Redis2.4以上的版本會確保在RDB快照創建時不觸發AOF重寫或者在AOF重寫時不允許BGSAVE操作,以避免Redis後臺進程同時做繁重的磁盤I/O操作。
當創建RDB快照時對於用戶使用BGREWRITEAOF明確發起的日誌重寫操作server會立刻迴應一個ok狀態碼告知用戶操作將回被執行,當且僅當快照創建完成後重寫操作開始被執行。
在同時使用了AOF和RDB方式的情況下,Redis重啓後會優先使用AOF文件來重構原始數據集。

備份Redis 數據

務必做好數據備份以防意外丟失。Redis是備份友好的,可在數據庫運行時拷貝RDB文件。建議的備份方案:
創建一個cron作業在一個目錄中每小時創建一次RDB快照在另一目錄中每天創建一次RDB快照;
cron作業每次運行的時候使用find命令確保過時的RDB快照文件被清理掉(可以通過在快照命中包含數據和時間信息來進行標記);
確保將RDB快照轉移到外部的數據中心或者至少是運行Redis實例的物理機之外的機器(至少每天一次)。

災難恢復
在Redis中災難恢復和數據備份基本上是同樣的過程。可考慮將備份分佈到不同的遠程數據中心以最大限度的避免數據丟失。幾種低成本的災難恢復計劃:
Amazon S3或其它類似服務是很好的選擇。可將每天會每小時的RDB快照以加密的方式(可使用gpg -c加密)傳輸到S3。確保將密碼存儲在不同的安全的地方。建議使用不同的存儲服務以提高數據安全性。
使用SCP命令將快照傳輸到遠程服務器。最簡單和安全的方式:獲取一個小的遠程VPS,在其上安裝ssh,生成無密碼的ssh client key添加到VPS的authorized_keys文件,此後便可使用SCP傳輸備份到VPS了。建議搞兩個不同的VPS以提高安全性。
需要注意的是,文件傳輸完成後一定要校驗文件的完整性正確性。可通過MD5或SHA1進行驗證。另外需要搭建一套告警系統,當備份傳輸發生問題時能及時的告知。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章