首頁  >  文章  >  資料庫  >  Redis持久化策略淺析

Redis持久化策略淺析

WBOY
WBOY轉載
2022-11-09 14:35:411791瀏覽

這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於持久化策略的相關內容,RDB持久化指的是在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,下面一起來看一下,希望對大家有幫助。

Redis持久化策略淺析

推薦學習:Redis影片教學

#Redis(Remote Dictionary Server ),即遠端字典服務,是一個開源的記憶體快取資料儲存服務。使用ANSI C 語言編寫,支援網路、可基於記憶體亦可持久化的日誌型、Key-Value 資料存儲,並提供多種語言的API

Redis 是記憶體資料庫,資料都是儲存在記憶體中,為了避免進程退出導致資料的永久遺失,需要定期將Redis 中的資料以某種形式(資料或命令)從記憶體保存到硬碟。當下次 Redis 重新啟動時,利用持久化檔案實現資料復原。除此之外,為了進行災難備份,可以將持久化文件拷貝到一個遠端位置。 Redis 的持久化機制有兩種:

  • RDB(Redis Data Base) 記憶體快照
  • AOF(Append Only File) 增量日誌

RDB 將目前資料儲存到硬碟,AOF 則是將每次執行的寫入指令儲存到硬碟(類似MySQL 的Binlog)。 AOF 持久化的即時性較好,即當進程意外退出時遺失的資料較少。

RDB 持久化

#簡介

RDB  (  Redis

 

Data Base)

 指的是指定的時間間隔內將記憶體中的資料集快照寫入磁碟,RDB 是記憶體快照(記憶體資料的二進位序列化形式)的方式持久化,每次都是從Redis 產生一個快照進行資料的全量備份。
  • 優點:
    儲存緊湊,節省記憶體空間。
    恢復速度非常快。

適合全量備份、全量複製的場景,經常用於災難復原(對資料的完整性和一致性要求相對較低的場合)。

  • 缺點:
  • 容易遺失數據,容易遺失兩次快照之間 Redis 伺服器中變化的數據。

RDB 透過 fork 子程序對記憶體快照進行全量備份,是一個重量級操作,頻繁執行成本高。 Redis持久化策略淺析

RDB 檔案結構

#在預設情況下,Redis 將資料庫快照保存在名字為dump.rdb 的二進位檔案中。 RDB 檔案結構由五個部分組成:

(1)長度為5位元組的 

REDIS

 常數字串。

(2)4位元組的 db_version,標識 RDB 檔案版本。

(3)databases:不定長度,包含零個或多個資料庫,以及各資料庫中的鍵值對資料。

(4)1位元組的 EOF 常數,表示檔案內文內容結束。

Redis持久化策略淺析(5)check_sum: 8位元組長的無符號整數,儲存校驗和。

Redis持久化策略淺析資料結構舉例,以下是資料庫[0]和資料庫[3]有資料的情況:

RDB 檔案的建立

手動指令觸發#手動觸發RDB 持久化的方式可以使用 save

 指令和 

bgsave 指令,這兩個指令的差異如下:save

:執行

 save 指令,阻塞Redis 的其他動作,會導致Redis 無法回應客戶端請求,不建議使用。 bgsave

:執行 

bgsave 指令,Redis 後台建立子程序,非同步進行快照的儲存操作,此時 Redis 仍能回應客戶端的請求。

自動間隔性保存

在預設情況下,Redis 將資料庫快照保存在名字為 dump.rdb的二進位檔案中。可以對 Redis 進行設置,讓它在「 N 秒內資料集至少有 M 個改動」這一條件被滿足時,自動保存一次資料集。

比如說, 以下設定會讓 Redis 在滿足「 60 秒內有至少有 10 個鍵被改動」這一條件時,自動儲存一次資料集:###save 60 10###。 ###

Redis 的預設配置如下,三個設定滿足其一即可觸發自動儲存:

save 60 10000
save 300 10
save 900 1

#自動儲存配置的資料結構

##記錄了伺服器觸發自動 

BGSAVE 條件的saveparams屬性。

lastsave 屬性:記錄伺服器最後一次執行 SAVE 或 BGSAVE# 的時間。

dirty 屬性:以及自從最後一次儲存 RDB 檔案以來,伺服器進行了多少次寫入。

Redis持久化策略淺析

備份過程

RDB 持久化方案進行備份時,Redis 會單獨fork 一個子程序來進行持久化,會將資料寫入一個臨時文件中,持久化完成後取代舊的RDB 檔案。在整個持久化過程中,主進程(為客戶端提供服務的進程)不參與IO 操作,這樣能確保Redis 服務的高效能,RDB 持久化機制適合對資料完整性要求不高但追求高效恢復的使用場景。以下展示RDB 持久化流程:

Redis持久化策略淺析

關鍵執行步驟如下

  • Redis 父程式先判斷:目前是否在執行save,或bgsave/bgrewriteaof 的子進程,如果在執行則bgsave 指令直接回傳。 bgsave/bgrewriteaof 的子程序不能同時執行,主要是基於效能方面的考量:兩個並發的子程序同時執行大量的磁碟寫入操作,可能會造成嚴重的效能問題。

  • 父進程執行 fork 操作建立子進程,這個過程中父進程是阻塞的,Redis 不能執行來自客戶端的任何命令。父進程 fork 後,bgsave 指令返回」Background saving started」訊息並不再阻塞父進程,並且可以回應其他指令。
  • 子進程對記憶體資料產生快照檔案。
  • 父進程在此期間接收的新的寫入操作,使用 COW 機制寫入。
  • 子程序完成快照寫入,取代舊 RDB 文件,隨後子程序退出。
在產生 RDB 檔案的步驟中,在同步到磁碟和持續寫入這個過程是如何處理資料不一致的情況呢?產生快照 RDB 檔案時是否會對業務產生影響?

Fork 子程序的作用

上面說到了RDB 持久化過程中,主程序會fork 一個子程序來負責RDB 的備份,這裡簡單介紹一下fork:

    Linux 作業系統中的程序,fork 會產生一個和父進程完全相同的子進程。子進程與父進程所有的資料一致,但是子進程是全新的進程,與原進程是父子進程關係。
    出於效率考慮,Linux 作業系統中使用COW(Copy On Write)寫時複製機制,fork 子進程一般情況下與父進程共同使用一段物理內存,只有在當進程空間中的記憶體發生修改時,記憶體空間才會複製一份出來。
在Redis 中,RDB 持久化就是充分的利用了這項技術,Redis 在持久化時調用glibc 函數fork 一個子進程,全權負責持久化工作,這樣父進程仍然能繼續給客戶端提供服務。 fork 的子進程初始時與父進程(Redis 的主進程)共享同一塊記憶體;當持久化過程中,客戶端的請求對記憶體中的資料進行修改,此時就會透過COW (Copy On Write) 機制對資料段頁面進行分離,也就是複製一塊記憶體出來給主進程修改。

Redis持久化策略淺析

透過fork 建立的子進程能夠獲得和父進程完全相同的記憶體空間,父進程對記憶體的修改對於子進程是不可見的,兩者不會相互影響;

透過fork 建立子進程時不會立刻觸發大量記憶體的拷貝,採用的是寫入時拷貝COW (Copy On Write)。核心只為新生成的子進程創建虛擬空間結構,它們來複製於父進程的虛擬究竟結構,但是不為這些段分配物理內存,它們共享父進程的物理空間,當父子進程中有更改相應段的行為發生時,再為子程序對應的段分配物理空間;

AOF 持久化

简介

AOF  (Append Only File)  是把所有对内存进行修改的指令(写操作)以独立日志文件的方式进行记录,重启时通过执行 AOF 文件中的 Redis 命令来恢复数据。类似MySql bin-log 原理。AOF 能够解决数据持久化实时性问题,是现在 Redis 持久化机制中主流的持久化方案。

Redis持久化策略淺析

优点:

  • 数据的备份更加完整,丢失数据的概率更低,适合对数据完整性要求高的场景
  • 日志文件可读,AOF 可操作性更强,可通过操作日志文件进行修复

缺点:

  • AOF 日志记录在长期运行中逐渐庞大,恢复起来非常耗时,需要定期对 AOF 日志进行瘦身处理
  • 恢复备份速度比较慢
  • 同步写操作频繁会带来性能压力

AOF 文件内容

被写入 AOF 文件的所有命令都是以 RESP 格式保存的,是纯文本格式保存在 AOF 文件中。

Redis 客户端和服务端之间使用一种名为 RESP(REdis Serialization Protocol) 的二进制安全文本协议进行通信。

下面以一个简单的 SET 命令进行举例:

redis> SET mykey "hello"    
//客户端命令OK

客户端封装为以下格式(每行用 \r\n分隔)

*3$3SET$5mykey$5hello

AOF 文件中记录的文本内容如下

*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n       
//多出一个SELECT 0 命令,用于指定数据库,为系统自动添加
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$5\r\nhello\r\n

AOF 持久化实现

AOF 持久化方案进行备份时,客户端所有请求的写命令都会被追加到 AOF 缓冲区中,缓冲区中的数据会根据 Redis 配置文件中配置的同步策略来同步到磁盘上的 AOF 文件中,追加保存每次写的操作到文件末尾。同时当 AOF 的文件达到重写策略配置的阈值时,Redis 会对 AOF 日志文件进行重写,给 AOF 日志文件瘦身。Redis 服务重启的时候,通过加载 AOF 日志文件来恢复数据。

Redis持久化策略淺析

AOF 的执行流程包括:

命令追加(append)

Redis 先将写命令追加到缓冲区 aof_buf,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘 IO 成为 Redis 负载的瓶颈。

struct redisServer {
   //其他域...   sds  aof_buf;           // sds类似于Java中的String   //其他域...}

文件写入(write)和文件同步(sync)

根据不同的同步策略将 aof_buf 中的内容同步到硬盘;

Linux 操作系统中为了提升性能,使用了页缓存(page cache)。当我们将 aof_buf 的内容写到磁盘上时,此时数据并没有真正的落盘,而是在 page cache 中,为了将 page cache 中的数据真正落盘,需要执行 fsync / fdatasync 命令来强制刷盘。这边的文件同步做的就是刷盘操作,或者叫文件刷盘可能更容易理解一些。

AOF 缓存区的同步文件策略由参数 appendfsync 控制,有三种同步策略,各个值的含义如下:

  • always:命令写入 aof_buf 后立即调用系统 write 操作和系统 fsync 操作同步到 AOF 文件,fsync 完成后线程返回。这种情况下,每次有写命令都要同步到 AOF 文件,硬盘 IO 成为性能瓶颈,Redis 只能支持大约几百TPS写入,严重降低了 Redis 的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低 SSD 的寿命。可靠性较高,数据基本不丢失。
  • no:命令写入 aof_buf 后调用系统 write 操作,不对 AOF 文件做 fsync 同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。
  • everysec:命令写入 aof_buf 后调用系统 write 操作,write 完成后线程返回;fsync 同步文件操作由专门的线程每秒调用一次。everysec 是前述两种策略的折中,是性能和数据安全性的平衡,因此是 Redis 的默认配置,也是我们推荐的配置。

文件重写(rewrite)

定期重写 AOF 文件,达到压缩的目的。

AOF 重写是 AOF 持久化的一个机制,用来压缩 AOF 文件,通过 fork 一个子进程,重新写一个新的 AOF 文件,该次重写不是读取旧的 AOF 文件进行复制,而是读取内存中的Redis数据库,重写一份 AOF 文件,有点类似于 RDB 的快照方式。

文件重写之所以能够压缩 AOF 文件,原因在于:

  • 过期的数据不再写入文件
  • 无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等
  • 多条命令可以合并为一个:如 sadd myset v1, sadd myset v2, sadd myset v3 可以合并为 sadd myset v1 v2 v3。不过为了防止单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset类型的 key,并不一定只使用一条命令;而是以某个常量为界将命令拆分为多条。这个常量在 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD 中定义,不可更改,2.9版本中值是64。

AOF 重写

前面提到 AOF 的缺点时,说过 AOF 属于日志追加的形式来存储 Redis 的写指令,这会导致大量冗余的指令存储,从而使得 AOF 日志文件非常庞大,比如同一个 key 被写了 10000 次,最后却被删除了,这种情况不仅占内存,也会导致恢复的时候非常缓慢,因此 Redis 提供重写机制来解决这个问题。Redis 的 AOF 持久化机制执行重写后,保存的只是恢复数据的最小指令集,我们如果想手动触发可以使用如下指令:

bgrewriteaof

文件重写时机

相关参数:

  • aof_current_size:表示当前 AOF 文件空间
  • aof_base_size:表示上一次重写后 AOF 文件空间
  • auto-aof-rewrite-min-size: 表示运行 AOF 重写时文件的最小体积,默认为64MB
  • auto-aof-rewrite-percentage: 表示当前 AOF 重写时文件空间(aof_current_size)超过上一次重写后 AOF 文件空间(aof_base_size)的比值多少后会重写。

同时满足下面两个条件,则触发 AOF 重写机制:

  • aof_current_size 大于 auto-aof-rewrite-min-size
  • 当前 AOF 相比上一次 AOF 的增长率:(aof_current_size - aof_base_size)/aof_base_size 大于或等于 auto-aof-rewrite-percentage

AOF 重写流程如下:

  • bgrewriteaof 触发重写,判断是否存在 bgsave 或者 bgrewriteaof 正在执行,存在则等待其执行结束再执行

  • 主进程 fork 子进程,防止主进程阻塞无法提供服务,类似 RDB
  • 子进程遍历 Redis 内存快照中数据写入临时 AOF 文件,同时会将新的写指令写入 aof_buf 和 aof_rewrite_buf 两个重写缓冲区,前者是为了写回旧的 AOF 文件,后者是为了后续刷新到临时 AOF 文件中,防止快照内存遍历时新的写入操作丢失

  • 子进程结束临时 AOF 文件写入后,通知主进程

  • 主进程会将上面 3 中的 aof_rewirte_buf 缓冲区中的数据写入到子进程生成的临时 AOF 文件中

  • 主进程使用临时 AOF 文件替换旧 AOF 文件,完成整个重写过程。

Redis持久化策略淺析

在实际中,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序会检查集合元素数量是否超过 REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值,如果超过了,则会使用多个命令来记录,而不单单使用一条命令。

Redis 2.9版本中该常量为64,如果一个命令的集合键包含超过了64个元素,重写程序会拆成多个命令。

Redis持久化策略淺析

AOF重写是一个有歧义的名字,该功能是通过直接读取数据库的键值对实现的,程序无需对现有AOF文件进行任何读入、分析或者写入操作。

思考

AOF 与 WAL

Redis 为什么考虑使用 AOF 而不是 WAL 呢?

很多数据库都是采用的 Write Ahead Log(WAL)写前日志,其特点就是先把修改的数据记录到日志中,再进行写数据的提交,可以方便通过日志进行数据恢复。

但是 Redis 采用的却是 AOF(Append Only File)写后日志,特点就是先执行写命令,把数据写入内存中,再记录日志。

如果先讓系統執行指令,只有指令能執行成功,才會被記錄到日誌中。因此,Redis 使用寫後日誌這種形式,可以避免出現記錄錯誤命令的情況。

另外還有一個原因是:AOF 是在指令執行後才記錄日誌,所以不會阻塞目前的寫入操作。

AOF 和 RDB 之間的互動

在版本編號大於等於2.4的 Redis 中,BGSAVE 執行的過程中,不可以執行 BGREWRITEAOF。反過來說,在 BGREWRITEAOF 執行的過程中,也不可以執行 BGSAVE。這可以防止兩個 Redis 後台程序同時對磁碟進行大量的 I/O 操作。

如果 BGSAVE 正在執行,並且用戶顯示地調用 BGREWRITEAOF 命令,那麼伺服器將向用戶回復一個 OK 狀態,並告知用戶,BGREWRITEAOF 已經被預定執行:一旦 BGSAVE 執行完畢 BGREWRITEAOF 就會正式開始。

當 Redis 啟動時,如果 RDB 持久化和 AOF 持久化都被打開了,那麼程式會優先使用 AOF 檔案來還原資料集,因為 AOF 檔案所保存的資料通常是最完整的。

混合持久化

Redis4.0 後來大部分的使用情境都不會單獨使用RDB 或AOF 來做持久化機制,而是兼顧二者的優勢混合使用。原因是 RDB 雖然快,但會遺失比較多的數據,不能保證數據完整性;AOF 雖然能盡可能保證數據完整性,但是性能確實是一個詬病,例如重播恢復數據。

Redis從4.0版本開始引入RDB-AOF 混合持久化模式,這個模式是基於AOF 持久化模式建構而來的,混合持久化透過 aof-use-rdb-preamble yes 開啟。

那麼Redis 伺服器在執行AOF 重寫操作時,就會像執行BGSAVE 指令那樣,根據資料庫目前的狀態產生出對應的RDB 數據,並將這些資料寫入新建的AOF 檔案中,至於那些在AOF 重寫開始之後執行的Redis 命令,則會繼續以協議文本的方式追加到新AOF 文件的末尾,即已有的RDB 數據的後面。

換句話說,在開啟了RDB-AOF 混合持久化功能之後,伺服器產生的AOF 檔案將由兩個部分組成,其中位於AOF 檔案開頭的是RDB 格式的數據,而跟在RDB 數據後面的則是AOF 格式的資料。

當一個支援 RDB-AOF 混合持久化模式的 Redis 伺服器啟動並載入 AOF 檔案時,它會檢查 AOF 檔案的開頭是否包含了 RDB 格式的內容。

  • 如果包含,那麼伺服器就會先載入開頭的 RDB 數據,然後再載入之後的 AOF 資料。
  • 如果 AOF 檔案只包含 AOF 數據,那麼伺服器將直接載入 AOF 資料。

其日誌檔案結構如下:

Redis持久化策略淺析

#總結

最後來總結這兩者,到底用哪個比較好呢?

  • 推薦是兩者皆開啟。
  • 如果對資料不敏感,可以選單獨用 RDB。
  • 如果只是做純記憶體緩存,可以都不用。

    推薦學習:Redis影片教學

    #

    以上是Redis持久化策略淺析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除