Redis筆記(三)Redis持久化

持久化的做用

什麼是持久化

Redis的數據操做都在內存中,redis崩掉的話,會丟失。Redis持久化就是對數據的更新異步的保存在磁盤上,以便數據恢復。java

持久化的實現方式

  • 快照方式
    對數據在某時某點的一種完整備份。例如Redis RDB,MySQL Dump都是這種方式。
  • 寫日誌方式
    任何數據的更新都記錄在日誌當中,某個時候要進行數據的恢復時,重走一遍日誌的完整過程。例如MySQL的Binlog,HBase的HLog和Redis的AOF,就是這種方式。

RDB

什麼是RDB

將Redis內存中的數據,完整的生成一個快照,以二進制格式文件(後綴RDB)保存在硬盤當中。當須要進行恢復時,再從硬盤加載到內存中。python

Redis主從複製,用的也是基於RDB方式,作一個複製文件的傳輸。ios

三種觸發方式

  • save命令觸發方式(同步)redis

    redis> save
    OK

    save執行時,會形成Redis的阻塞。全部數據操做命令都要排隊等待它完成。
    文件策略:新生成一個新的臨時文件,當save執行完後,用新的替換老的。vim

  • bgsave命令觸發方式(異步)緩存

    redis> bgsave
    Background saving started

    客戶端對Redis服務器下達bgsave命令時,Redis會fork出一個子進程進行RDB文件的生成。當RDB生成完畢後,子進程再反饋給主進程。fork子進程時也會阻塞,不過正常狀況下fork過程都很是快的。
    文件策略:與save命令相同。
    save與bgsave對比:安全

    命令 save bgsave
    IO類型 同步 異步
    阻塞? 是(發生在fork期間)
    複雜度 O(n) O(n)
    優勢 不消耗額外內存 不阻塞客戶端命令
    缺點 阻塞客戶端命令 fork消耗額外內存
  • 規則自動觸發方式
    某些條件達到時,自動生成RDB文件。
    好比咱們配置以下:服務器

    配置 seconds changes 說明
    save 900 1 900秒內改變1條數據,自動生成RDB文件
    save 300 10 300秒內改變10條數據,自動生成RDB文件
    save 60 10000 60秒內改變1萬條數據,自動生成RDB文件

    以上任一條件達到時,都會觸發生成RDB文件。不過這種方式對RDB文件的生成頻率不太好控制。若是寫量大,RDB生成會很頻繁。不是一種好的方式。
    修改配置文件:網絡

    # 配置自動生成規則。通常不建議配置自動生成RDB文件
    save 900 1
    save 300 10
    save 60 10000
    # 指定rdb文件名
    dbfilename dump-${port}.rdb
    # 指定rdb文件目錄
    dir /opt/redis/data
    # bgsave發生錯誤,中止寫入
    stop-writes-on-bgsave-error yes
    # rdb文件採用壓縮格式
    rdbcompression yes
    # 對rdb文件進行校驗
    rdbchecksum yes

不容忽略的觸發方式

  1. 全量複製
    主從複製時,主會自動生成RDB文件。
  2. debug reload
    Redis中的debug reload提供debug級別的重啓,不清空內存的一種重啓,這種方式也會觸發RDB文件的生成。
  3. shutdown
    會觸發RDB文件的生成。

試驗

save試驗app

cd redis
cd config
cp ../redis.conf
cp redis.conf redis-6379.conf
vim redis-6379.conf

修改以下配置:

daemonize yes
pidfile /var/run/redis-6379.pid
port 6379
logfile "6379.log"
# 先關閉自動生成RDB的配置
# save 900 1
# save 300 10
# save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump-6379.rdb
dir /opt/soft/redis/data

保存配置

:wq
# 重啓redis
redis-server redis-6379.conf
# 客戶端鏈接
redis-cli
# 暫時尚未Redis內存數據
127.0.0.1:6379> dbsize
(integer) 0

而後寫個簡單for循環程序,往Redis執行大量的寫命令,讓內存數據足夠大。

# 再看一下,這下內存數據有不少了
127.0.0.1:6379> dbsize
(integer) 5000000
# 看下內存使用了904M,足夠用以演示save的阻塞了
127.0.0.1:6379> info memory
used_memory: 948306016
used_memory_human: 904.38M
used_memory_rss: 1031897088
used_memory_peak: 981827104
used_memory_peak_human: 936.34M
used_memory_lua: 36864
mem_fragmentation_ratio: 1.09
mem_allocator: libc
# 執行save,發現等待了若干秒後,才輸出OK以及消耗時間
127.0.0.1:6379> save
OK
(8.94s)

/opt/soft/redis/data目錄下也會生成dump-6379.rdb文件。

bgsave試驗
驗證bgsave的非阻塞:

# 咱們再開一個新窗口,在新窗口上鍊接redis客戶端
redis-cli
# 輸好如下命令,先別執行
127.0.0.1:6379> get hello
# 而後在原窗口執行bgsave
127.0.0.1:6379> bgsave
Background saving started
# 立刻切回新窗口,回車執行命令,發現world即刻返回,驗證了bgsave的非阻塞
127.0.0.1:6379> get hello
"world"

接下來驗證bgsave會生成子進程:

# 在新窗口先查看下redis進程,過濾掉客戶端和grep進程,發現就只有一個redis主進程
ps -ef | grep redis- | grep -v "redis-cli" | grep -v "grep"
501 36775     1   0 10:22下午 ??      0:17 .86 redis-server *:6379
# 在原窗口再執行一次bgsave
127.0.0.1:6379> bgsave
Background saving started
# 立刻切新窗口再次查看進程,發現多了個子進程redis-rdb-bgsave
ps -ef | grep redis- | grep -v "redis-cli" | grep -v "grep"
501 36775     1   0 10:22下午 ??      0:17 .91 redis-server *:6379
501 36954     1   0 10:28下午 ??      0:02 .81 redis-rdb-bgsave *:6379
# 再看一次,子進程已經不在了。由於子進程已經完成了它生成rdb文件的工做
ps -ef | grep redis- | grep -v "redis-cli" | grep -v "grep"
501 36775     1   0 10:22下午 ??      0:17 .91 redis-server *:6379

最後驗證文件策略:

# 在新窗口/data目錄查看文件
ls
6379.log dump-6379.log
# 在原窗口再執行一次bgsave
127.0.0.1:6379> bgsave
Background saving started
# 切新窗口再次查看文件,多了個臨時的rdb文件
ls
6379.log dump-6379.rdb temp-36985.rdb
# 過會兒再查看一次,臨時文件消失了
ls
6379.log dump-6379.rdb

自動生成試驗
這個不演示了,本身修改配置文件save 60 5,配置60秒更新5次就自動生成RDB文件。重啓redis後,咱們在客戶端用set命令執行5次。觀察/data下的rdb文件的時間戳是否變化了來驗證。

咱們也能夠查看下日誌文件6379.log,輸出了試驗過程的相關日誌內容。

RDB總結

  1. RDB是Redis內存到硬盤的快照,用於持久化;
  2. save一般會阻塞redis;
  3. bgsave一般不會阻塞redis,可是會fork新進程;
  4. save自動配置知足任一就會被執行;
  5. 有些觸發機制不容忽視。

AOF

RDB存在的問題

  • 耗時、耗內存、耗IO性能
    將內存中的數據所有dump到硬盤當中,耗時。bgsave的方式fork()子進程耗額外內存。大量的硬盤讀寫耗費IO性能。
  • 不可控、丟失數據
    宕機時,上次快照以後寫入的內存數據,將會丟失。

什麼是AOF

就是寫日誌,每次執行Redis寫命令,讓命令同時記錄日誌(以AOF日誌格式)。Redis宕機時,只要進行日誌回放就能夠恢復數據。

AOF三種策略

首先Redis執行寫命令,將命令刷新到硬盤緩衝區當中。

  • always
    always策略讓緩衝區中的數據即時刷新到硬盤。
  • everysec
    everysec策略讓緩衝區中的數據每秒刷新到硬盤。相比always,在高寫入量的狀況下,能夠保護硬盤。出現故障可能會丟失一秒數據。
  • no
    刷新策略讓操做系統來決定。

三種策略對比

命令 always everysec no
優勢 不丟失數據 只丟一秒數據 不用管,操做系統去管
缺點 IO開銷大,通常的sata盤只有幾百TPS 丟了一秒數據 不可控,不知道何時刷盤,也不知道會丟失多少數據

一般使用everysec策略,這也是AOF的默認策略。

AOF重寫

隨着時間的推移,命令的逐步寫入。AOF文件也會逐漸變大。當咱們用AOF來恢復時會很慢,並且當文件無限增大時,對硬盤的管理,對寫入的速度也會有產生影響。Redis固然考慮到這個問題,因此就有了AOF重寫。

原生AOF:

set hello world
set hello java
set hello python
incr counter
incr counter
rpush mylist a
rpush mylist b
rpush mylist c
過時數據

重寫後的AOF:

set hello python
set incr 2
rpush mylist a b c

AOF重寫就是把過時的、沒用的、重複的以及可優化的命令,進行化簡。只取最終有價值的結果。雖然寫入操做很頻繁,但系統定義的key的量是相對有限的。

AOF重寫能夠大大壓縮最終日誌文件的大小。從而減小磁盤佔用量,加快數據恢復速度。好比咱們有個計數的服務,有不少自增的操做,好比有一個key自增到1個億,對AOF文件來講就是一億次incr。AOF重寫就只用記1條記錄。

AOF重寫兩種方式

  • bgrewriteaof命令觸發AOF重寫
    redis客戶端向Redis發bgrewriteaof命令,redis服務端fork一個子進程去完成AOF重寫。這裏的AOF重寫,是將Redis內存中的數據進行一次回溯,回溯成AOF文件。而不是重寫AOF文件生成新的AOF文件去替換。
  • AOF重寫配置
    auto-aof-rewrite-min-size:AOF文件重寫須要的尺寸
    auto-aof-rewrite-percentage:AOF文件增加率
    redis提供了aof_current_size和aof_base_size,分別用來統計AOF當前尺寸(單位:字節)和AOF上次啓動和重寫的尺寸(單位:字節)。
    AOF自動重寫的觸發時機,同時知足如下兩點):

    • aof_current_size > auto-aof-rewrite-min-size
    • aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage

AOF重寫配置

修改配置文件:

# 開啓正常AOF的append刷盤操做
appendonly yes
# AOF文件名
appendfilename "appendonly-6379.aof"
# 每秒刷盤
appendfsync everysec
# 文件目錄
dir /opt/soft/redis/data
# AOF重寫增加率
auto-aof-rewrite-percentage 100
# AOF重寫最小尺寸
auto-aof-rewrite-min-size 64mb
# AOF重寫期間是否暫停append操做。AOF重寫很是消耗磁盤性能,而正常的AOF過程當中也會往磁盤刷數據。
# 一般偏向考慮性能,設爲yes。萬一重寫失敗了,這期間正常AOF的數據會丟失,由於咱們選擇了重寫期間放棄了正常AOF刷盤。
no-appendfsync-on-rewrite yes

試驗

redis-cli
127.0.0.1:6379> dbsize
(integer) 5000000
127.0.0.1:6379> exit
vim redis-6379.conf
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes

配置修改後,要從新啓動redis。
appendonly是支持動態配置,不用重啓Redis:

127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379> config set appendonly yes
OK
# 從新加載配置
127.0.0.1:6379> config rewrite
OK
127.0.0.1:6379> exit

先試驗下正常AOF刷盤

# 客戶端鏈接redis,執行一些命令:
redis-cli
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set hello java
OK
127.0.0.1:6379> set hello redis
OK
127.0.0.1:6379> incr counter
(integer) 1
127.0.0.1:6379> incr counter
(integer) 2
127.0.0.1:6379> rpush list a
(integer) 1
127.0.0.1:6379> rpush list b
(integer) 2
127.0.0.1:6379> rpush list c
(integer) 3
127.0.0.1:6379> exit
# 咱們查看data目錄,appendonly.aof文件已經生成了
cd /opt/soft/redis/data
ll
-rw-r--r--   1   carlosfu  staff   16K  10  7  22:28  6379.log
-rw-r--r--   1   carlosfu  staff  243B  10  7  22:29  appendonly.aof
-rw-r--r--   1   carlosfu  staff   18B  10  7  22:19  dump-6379.rdb

再試驗下AOF重寫

redis-cli
127.0.0.1:6379> bgrewriteaof
Background append only file rewriteing started
127.0.0.1:6379> dbsize
(integer) 3
# 咱們再查看data目錄,appendonly.aof文件變小了
cd /opt/soft/redis/data
ll
-rw-r--r--   1   carlosfu  staff   17K  10  7  22:33  6379.log
-rw-r--r--   1   carlosfu  staff  137B  10  7  22:33  appendonly.aof
-rw-r--r--   1   carlosfu  staff   18B  10  7  22:19  dump-6379.rdb

RDB和AOF的抉擇

RDB和AOF的比較

命令 RDB AOF 說明
啓動優先級 RDB和AOF都開啓的狀況下,Redis重啓後,選擇AOF進行恢復。大部分狀況下它保存了比RDB更新的數據
體積 RDB二進制模式存儲,並且作了壓縮。AOF雖然有AOF重寫,可是體積相對仍是大不少,畢竟它是記日誌形式
恢復速度 RDB體積小,恢復速度快。AOF體積大,恢復速度慢
數據安全 丟數據 根據策略決定 RDB丟上次快照後的數據,AOF根據always、everysec、no策略決定是否丟數據
輕重 AOF是追加日誌,因此比較輕的操做。而RDB是CPU密集型操做,對磁盤,以及fork時對內存的消耗都比較大

RDB最佳策略

  1. 建議關閉RDB
    不管是Redis主節點,仍是從節點,都建議關掉RDB。可是關掉不是絕對的,主從複製時仍是會藉助RDB。
  2. 用做數據備份
    RDB雖然是很重的操做,可是對數據備份頗有做用。文件大小比較小,能夠按天或按小時進行數據備份。
  3. 主從,從開?
    在極個別的場景下,須要在從節點開RDB,能夠再本地保存這樣子的一個歷史的RDB文件。雖然從節點不進行讀寫,可是Redis每每單機多部署,因爲RDB是個很重的操做,因此仍是會對CPU、硬盤和內存形成必定影響。根據實際需求進行設定。

AOF最佳策略

  1. 建議開啓AOF
    若是Redis數據只是用做數據源的緩存,而且緩存丟失後從數據源從新加載不會對數據源形成太大壓力,這種狀況下。AOF能夠關。
  2. AOF重寫集中管理
    單機多部署狀況下,發生大量fork可能會內存爆滿。
  3. everysec
    建議採用每秒刷盤策略

最佳策略

  1. 小分片
    使用maxmemary對Redis最大內存進行規劃。
  2. 緩存和存儲
    根據緩存和存儲的特性來決定使用哪一種策略
  3. 監控(硬盤、內存、負載、網絡)
  4. 足夠的內存
    不要把就機器所有的內存規劃給Redis。否則會出不少問題。像客戶端緩衝區等,不受maxmemary限制。規劃不當可能會產生SWAP、OOM等問題。

開發運維常見問題

fork操做

fork是一個同步操做。執行bgsave和bgrewriteaof時都會執行fork操做,

改善fork

  1. 優先使用物理機或者其餘能高效支持form操做的虛擬化技術;
  2. 控制Redis實例最大可用內存maxmemary;
    fork操做只是執行內存頁的拷貝,大部分狀況速度是比較快的。redis內存越大,內存頁越大。可使用maxmemary規劃redis內存,避免fork過慢。
  3. 合理配置Linux內存分配策略:vm.overcommit_memory=1
    fork時若是內存不夠,會阻塞。Linux的vm.overcommit_memory默認爲0,不會分配額外內存

子進程開銷和優化

bgsave和bgrewriteaof會進行fork操做產生子進程。

  1. CPU

    • 開銷:RDB和AOF文件生成屬於CPU密集型;
    • 優化:不作CPU綁定,不和CPU密集型應用部署在一塊兒;
  2. 內存

    • 開銷:fork內存開銷
    • 優化:echo never > /sys/kernel/mm/transparent_hugepage/enabled
  3. 硬盤

    • 開銷:AOF和RDB文件寫入,能夠結合iostat和iotao分析
    • 優化:

      1. 不要和高硬盤負載服務部署在一塊兒:存儲服務、消息隊列;
      2. no-appendfsync-on-rewrite=yes;
      3. 根據寫入量決定磁盤類型:例如sdd;
      4. 單機多實例持久化文件目錄能夠考慮分盤;

AOF追加阻塞

AOF阻塞定位

  1. redis日誌:

    Asynchronous AOF fsync is taking to long(disk is busy?). Writing the AOF 
    buffer whitout waiting for fsync to complete, this may slow down Redis
  2. info persistence
    能夠查看上述日誌發生的次數。

    127.0.0.1:6379> info persistence
    ......
    ......
    aof_delayed_fsync: 100
    ......
    ......

改善方式 同子進程的硬盤優化

相關文章
相關標籤/搜索