Redis拔高篇——Redis的持久化

Redis持久化

爲什麼Redis存在持久化

在我的windows下,使用完Redis之後關閉,數據仍然存在,甚至在過了幾個月後redis中的數據仍然存在。沒錯,當我們在本地裝了redis,並且在redis中寫了數據,重啓計算機後數據依然會存在。

但是學習Redis的時候,使用了Linux,我發現了一個問題,打開一次redis-server,存儲、獲取數據都是正常的,但是關閉之後重啓,數據全都丟了,我的媽呀,what happened?

後來查詢了很多博客,我明白了,由於 Redis 是一個內存數據庫,所謂內存數據庫,就是將數據庫中的內容保存在內存中,這與傳統的MySQL,Oracle等關係型數據庫直接將內容保存到硬盤中相比,內存數據庫的讀寫效率比傳統數據庫要快的多(內存的讀寫效率遠遠大於硬盤的讀寫效率)。但是保存在內存中也隨之帶來了一個缺點,一旦斷電或者宕機,那麼內存數據庫中的數據將會全部丟失。所以,redis如果沒有做持久化,在重啓redis後,數據會丟失。

RDB

RDB基本介紹

RDB是Redis用來進行持久化的一種方式,是把當前內存中的數據集快照寫入磁盤,也就是 Snapshot 快照(數據庫中所有鍵值對數據)。恢復時是將快照文件直接讀到內存裏。

RDB啓動方式——手動保存

在數據庫操作的時候手動輸入save,進行數據庫的一次保存。
在這裏插入圖片描述

配置文件的內容:(配置文件的位置:redis-4.0.0/conf/redis.conf

# 指定啓動的端口
port 6379
# 設置是否後臺啓動
daemonize yes
# 設置日誌文件存的文件的名稱
logfile "6379.log"
# 設置.rdb文件的存儲路徑
dir /usr/local/redis/redis-4.0.0/data
# 設置rdb文件的名稱
dbfilename dump.rdb

然後data目錄下生成的dump.rdb文件當中的數據如下:(正常人應該都看不懂,但是部分的東西還是看的懂的,最起碼數據xiaobai存在)
在這裏插入圖片描述
但是我們不能保證每次都會把輸入的數據存儲下來,在數據量大的時候難免會忘記save數據, 那麼有沒有一種方式可以把數據保存到磁盤,這樣我們的注意力就可以都放在數據上了。

RDB啓動方式——save自動執行配置
  • 配置:save second changes
  • 作用:滿足限定時間範圍內key的變化數量達到指定數量即進行持久化
  • 參數:second:監控時間範圍 | changes:監控key的變化量A
  • 位置:在conf文件中進行配置
  • 範例:
    save 900 1
    save 300 10
    save 60 10000
    在這裏插入圖片描述

配置文件內容

# 指定啓動的端口
port 6379
# 設置是否後臺啓動
daemonize yes
# 設置日誌文件存的文件的名稱
logfile "6379.log"
# 設置.rdb文件的存儲路徑
dir /usr/local/redis/redis-4.0.0/data
# 設置rdb文件名稱
dbfilename dump.rdb
# save <second> <changes>
save 10 2

second是時間限制,changes是改變的次數,這個配置的含義就是在規定的時間內,數據改變的次數達到changes,自動執行一次持久化,來看一下演示:

save配置原理
在這裏插入圖片描述
設置數據
在這裏插入圖片描述
執行結果
在這裏插入圖片描述
上面的圖證明,只有在對數據庫當中的數據進行更改的時候,才執行持久化操作,像get這種查詢語句,對數據沒有一點兒影響的操作是不會觸發save操作的。像是重寫,修改,增加字段這些操作,對redis當中的數據產生了影響,所以在一定的時間內發生指定次數的修改,就會執行save操作。

思考(自己的理解,希望大家幫忙糾正)

1、當固定時間內,發生修改的次數不夠怎麼辦?
答:不滿足就繼續往下計時,直到次數夠了,執行save操作,把數據寫入rdb文件,計時時間歸零,繼續下一次計時
2、當固定的時間內,發生的次數過多怎麼辦?
答:達到指定的次數,執行save操作,計時歸零

繼續想一個問題,因爲redis是單線程,那麼也就是說redis的各種操作和持久化是在同一個線程當中執行的,那麼如果訪問量特別高,花費在IO上的時間也是相當多的,效率也是低的,那麼有沒有另外一種方式,可以提高redis的利用效率呢?我們自己想的話,可能想到單開一個線程進行IO讀寫,這樣持久化的操作就不會佔用主線程,利用效率也就提升上來了。

Redis的開發者也想到了這個問題,所以給持久化操作提供了一種新的方法。

RDB啓動方式——bgsave後臺執行

配置文件內容

# 指定啓動的端口
port 6379
# 設置是否後臺啓動
daemonize yes
# 設置日誌文件存的文件的名稱
logfile "6379.log"
# 設置.rdb文件的存儲路徑
dir /usr/local/redis/redis-4.0.0/data
# 設置rdb文件名稱
dbfilename dump.rdb
# rdb存儲到本地是否壓縮,默認yes,採用LZF壓縮
rdbcompression yes
# 是否進行RDB文件格式校驗,寫文件和讀文件的時候都進行
rdbchecksum yes 
# save <second> <changes>
save 10 2

代碼執行樣例:
在這裏插入圖片描述

bgsave執行流程
在這裏插入圖片描述
1、輸入bgsave指令之後,向redis數據庫發送
2、redis調度fork函數生成子進程,同時向客戶端發送Background saving started信息
3、子進程找到一個時間執行,之後創建.rdb文件
4、子進程運行完畢,向父進程發送執行完畢消息

bgsave命令是針對save在大量的持久化時發生的阻塞問題做的優化。
學到這裏redis內部所有涉及到RDB操作都可以採用bgsave的方式,save命令可以放棄使用。

RDB特殊啓動形式
  • 服務器運行過程中重啓
    debug reload
  • 關閉服務器時指定保存數據
    shutdown save
RDB總結

save和bgsave啓動方式的對比

方式 save指令 bgsave指令
讀寫 同步 異步
阻塞客戶端指令 阻塞 不阻塞
額外內存消耗
啓動新進程

1、bgsave保存數據的時候,是單獨開一個線程,所以是異步的
2、save的所有操作都是在一個進程當中執行的,保存數據的過程涉及到的IO操作會阻塞線程,讓客戶端發生延遲
3、save所有的操作都是在單獨的線程之內,沒有額外內存消耗;但是bgsave開一個新的線程,額外佔用空間

RDB優點

  • RDB是一個緊湊壓縮的二進制文件,存儲效率較高
  • RDB內部存儲的是redis在某個時間點的數據快照,非常適合用於數據備份,全量複製等場景
  • RDB恢復數據的速度要比AOF快很多
  • 應用:服務器中每X小時執行bgsave備份,並將RDB文件拷貝到遠程機器中,用於災難恢復。

RDB缺點

  • RDB方式無論是執行指令還是利用配置,無法做到實時持久化,具有較大的可能性丟失數據
  • bgsave指令每次運行要執行fork操作創建子進程,要犧牲掉一些性能
  • Redis的衆多版本中未進行RDB文件格式的版本統一, 有可能出現各版本服務之間數據格式無法兼容現象
注意

1、save配置要根據實際業務情況進行設置,頻度過高或過低都會出現性能問題,結果可能是災難性的
2、save配置中對於second與changes設置通常具有互補對應關係,儘量不要設置成包含性關係
3、save配置啓動後執行的是bgsave操作

AOF

AOF的概念
  • AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啓時再重新執行AOF文件中命令達到恢復數據的目的。與RDB相比可以簡單描述爲改記錄數據爲記錄數據產生的過程
  • AOF的主要作用是解決了數據持久化的實時性,目前已經是Redis持久化的主流方式
RDB的缺點

RDB雖然比較強大,但是也是存在一些不足的,現在再次總結一下:

  • 存儲數據量較大,效率較低,基於快照思想,每次讀寫都是全部數據,當數據量巨大時,效率非常低
  • 大數據量下的IO性能較低
  • 執行bgsave指令的時候,基於fork創建子進程,內存產生額外消耗
  • 假如說服務器宕機了,那麼子線程也會結束,數據就存在着丟失風險
AOF執行流程

1、在執行增加或者是修改字段操作之後,向redis發送這些指令
2、之後redis把這些指令寫到AOF寫命令刷新緩衝區當中,
3、同步緩衝區的數據到.aof文件當中

AOF寫數據三種策略(appendfsync)
  • always(每次)
    每次寫入操作均同步到AOF文件中,數據零誤差,性能較低,不建議使用。
  • everysec (每秒)
    每秒將緩衝區中的指令同步到AOF文件中,數據準確性較高,性能較高,建議使用,也是默認配置在系統突然宕機的情況下丟失1秒內的數據
  • no (系統控制)
    由操作系統控制每次同步到AOF文件的週期,整體過程不可控

叭叭叭說一堆,那到底是怎麼操作呢??別急,這不來了

AOF功能開啓
  • 配置
    appendonly yes| no
  • 作用
    是否開啓AOF持久化功能,默認爲不開啓狀態
  • 配置
    appendfsync | aIways I everysec I no
  • 作用
    AOF寫數據策略

配置文件內容

# 指定啓動的端口
port 6379
# 設置是否後臺啓動
daemonize yes
# 設置日誌文件存的文件的名稱
logfile "6379.log"
# 設置.rdb文件的存儲路徑
dir /usr/local/redis/redis-4.0.0/data
# 是否開啓aof持久化功能,默認是no
appendonly yes
# 指定aof寫策略
appendfsync everysec

操作演示
在這裏插入圖片描述
生成的appendonly.aof文件的內容:
在這裏插入圖片描述

.aof文件把我們所有的操作都記錄下來,但是思考一個問題,要是我進行下面這些操作:

127.0.0.1:6379> set name xiaohei
OK
127.0.0.1:6379> set name black
OK
127.0.0.1:6379> set name white
OK
127.0.0.1:6379> incrby age 1
(integer) 12
127.0.0.1:6379> incrby age 1
(integer) 13
127.0.0.1:6379> incrby age 1
(integer) 14
127.0.0.1:6379> incrby age 1
(integer) 15

對於這樣的重複操作,appendonly.aof文件會怎樣記錄呢?查詢一下:
在這裏插入圖片描述
上面的內容暫且不提,就光看最後的這幾行數據,就知道incr的4次操作都記錄下來了,其實也挺好,每一步的清清楚楚,但是這假如說我寫一個統點贊數量的功能,那得記錄多少條?文件可是相當大的,這讓本來就不充足的服務器空間更加的緊緊巴巴,那麼可不可以簡化一下,比如說我incrby四次,之後aof文件記錄的是incrby age 4,一條頂四條,那麼aof文件所佔用的空間就會大大減少。

AOF重寫

隨着命令不斷寫入AOF,文件會越來越大,爲了解決這個問題,Redis引入 了AOF重寫機制壓縮文件體積。AOF文件重寫是將Redis進程內的數據轉化爲寫命令同步到新AOF文件的過程。簡單說就是將對同一個數據的若千個條命令執行結果轉化成最終結果數據對應的指令進行記錄。

AOF重寫作用
  • 降低磁盤佔用量,提高磁盤利用率
  • 提高持久化效率,降低持久化寫時間,提高lO性能
  • 降低數據恢復用時,提高數據恢復效率
AOF重寫規則
  • 進程內已超時的數據不再寫入文件
  • 忽略無效指令,重寫時使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令
    如del key1、hdel key2、srem key3、setkey4 111、set key4 222等
  • 對同一數據的多條寫命令合併爲一條命令
    如lpush list1 a. lpush list1 b、Ipush list1c可以轉化爲: lpushlist1abc.
    爲防止數據量過大造成客戶端緩衝區溢出,對list. set. hash, zset等類型,每條指令最多寫入64個元素
AOF手動重寫

指令:bgrewriteaof
操作演示:(緊接着查看aof文件內容)
在這裏插入圖片描述
全都變成設置字段時候的狀態,這就大大節省了存儲空間。

AOF手動重寫流程

在這裏插入圖片描述
1、輸入bgrewriteaof指令之後,向redis數據庫發送指令
2、redis調度fork函數生成子進程,同時向客戶端發送Background append only file rewriting started信息
3、子進程找到一個時間執行,之後重寫.aof文件
4、子進程運行完畢,向父進程發送執行完畢消息

AOF自動重寫方式

既然RDB存在自動執行保存數據的bgsave,那麼重寫aof肯定也存在一個自動執行的方式。

  • 自動重寫觸發條件設置
    auto-aof-rewrite-min-size size
    auto-aof-rewrite-percentage percent
  • 自動重寫觸發比對參數(運行指令info Persistence獲取具體信息)
    aof_current_size
    aof_base_size
  • 自動重寫觸發條件
    aof_current_size>auto-aof-rewrite-min-size
    (aof_current_size - aof_base_size) / (aof_base_size) >= auto-aof-rewrite-percentage

那麼稍微修改一下配置文件內容

# 指定啓動的端口
port 6379
# 設置是否後臺啓動
daemonize yes
# 設置日誌文件存的文件的名稱
logfile "6379.log"
# 設置.rdb文件的存儲路徑
dir /usr/local/redis/redis-4.0.0/data
dbfilename dump.rdb
# rdb存儲到本地是否壓縮,默認yes,採用LZF壓縮
rdbcompression yes
# 是否進行RDB文件格式校驗,寫文件和讀文件的時候都進行
rdbchecksum yes 
# save <second> <changes>
save 10 2
# 是否開啓aof持久化功能,默認是no
appendonly yes
# 指定aof寫策略
appendfsync everysec
# 設置最小的自動重寫aof文件的長度
auto-aof-rewrite-min-size 300
# 設置aof文件的名稱
appendfilename appendonly-6379.aof

在這裏插入圖片描述

AOF重寫流程(詳細)

在這裏插入圖片描述
1、按照always配置,執行set指令的時候,主進程執行set指令,同時啓動fork進程,子進程把set操作寫到非重寫.aof文件當中
2、按照everysec配置,當執行set指令的時候,主進程生成fork進程,同時執行set指令,fork新進程把set指令寫到.aof緩衝區當中,當滿足某個條件的時候,緩衝區當中的數據一併寫到非重寫.aof文件當中。
3、基於everysec開啓重寫,當執行set指令的時候,主進程生成fork進程,同時執行set指令,fork新進程把set指令寫到.aof緩衝區和.aof重寫緩衝區當中,aof緩衝區滿足某個條件的時候,aof緩衝區當中的數據一併寫到非重寫.aof文件當中,當滿足某個重寫的條件的時候,aof重寫緩衝區的內容重寫.aof文件的內容。
在這裏插入圖片描述
4、執行bgrewriteaof重寫指令的時候,主進程開啓fork進程,同時返回bgwriteaof的提示信息,fork生成的子進程執行重寫aof文件的操作,之後生成新的aof文件,最後和原來的aof文件進行合併替換。

上面寫的aof重寫緩衝區重寫aof文件的時候,提供數據,生成新的aof文件,最後也是和原來的aof文件進行合併替換

總結:AOF和RDB的區別

我學到的東西都寫到這篇博客當中了,現在總結一下這兩種持久化的方式有哪些區別。

持久化方式 RDB AOF
佔用存儲空間 小(存儲數據集) 大(存儲指令集)
存儲速度
恢復速度
數據安全性 會丟失數據 根據策略決定
資源消耗 高/重量級 低/輕量級
啓動優先級
持久化方式RDB和AOF的選擇
  • 對數據非常敏感,建議使用默認的AOF持久化方案,就像根本不允許丟失數據或者只允許丟失很少一部分數據的項目,需要使用AOF進行持久化

  • AOF持久化策略使用everysecond,每秒鐘fsync一次。 該策略redis仍可以保持很好的處理性能,當出現問題時,最多丟失0- 1秒內的數據。
    注意:由於AOF文件存儲體積較大,且恢復速度較慢

  • 數據呈現階段有效性,建議使用RDB持久化方案

  • 數據可以良好的做到階段內無丟失(該階段是開發者或運維人員手工維護的),且恢復速度較快,階段點數據恢復通常採用RDB方案
    注意:利用RDB實現緊湊的數據持久化會使Redis降的很低

綜合比對

  • RDB與AOF的選擇實際上是在做一種權衡,每種都有利有弊
  • 如不能承受數分鐘以內的數據丟失,對業務數據非常敏感,選用AOF
  • 如能承受數分鐘以內的數據丟失,且追求大數據集的恢復速度,選用RDB
  • 災難恢復選用RDB
  • 雙保險策略,同時開啓RDB和AOF,重啓後,Redis優先使用AOF來恢復數據,降低丟失數據的量
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章