首頁 >資料庫 >Redis >完全掌握Redis持久化:RDB和AOF

完全掌握Redis持久化:RDB和AOF

WBOY
WBOY轉載
2022-06-16 12:10:461974瀏覽

這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於持久化的相關問題,包括了為什麼需要持久化、RDB持久化、AOF持久化等等內容,下面一起來看一下,希望對大家有幫助。

完全掌握Redis持久化:RDB和AOF

推薦學習:Redis影片教學

一、為什麼需要持久化?

Redis對資料的操作都是基於記憶體的,當遇到了進程退出、伺服器當機等意外情況,如果沒有持久化機制,那麼Redis中的資料將會遺失無法恢復。有了持久化機制,Redis在下次重啟時可以利用先前持久化的檔案進行資料恢復。 Redis支援的兩種持久化機制:

RDB:把目前資料產生快照保存在硬碟上。
AOF:記錄每次對資料的操作到硬碟上。

二、RDB持久化

在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,它還原時是將快照檔案直接讀到內存裡。 RDB(Redis DataBase)持久化是把目前Redis中全部資料產生快照保存在硬碟上。 RDB持久化可以手動觸發,也可以自動觸發。

1、備份是如何執行的?

redis會單獨建立(fork)一個子程序來進行持久化,會先將資料寫入到暫存器中,待持久化過程都結束了,再用這個暫存檔案取代上次持久化好了的文件。整個過程中,主進程是不進行任何IO操作的,這確保了極高的效能。如果需要進行大規模資料的恢復,且對資料的恢復完整性不是非常敏感,那麼RDB方式要比AOF方式更加的高效。 RDB的缺點是最後一次持久化的資料可能會遺失。

2、RDB持久化流程

完全掌握Redis持久化:RDB和AOF

3、手動觸發

savebgsave指令都可以手動觸發RDB持久化。

  1. save
    # 執行save指令會手動觸發RDB持久化,但save指令會阻塞Redis服務,直到RDB持久化完成。當Redis服務儲存大量資料時,會造成較長時間的阻塞,不建議使用。
  2. bgsave
    執行bgsave指令也會手動觸發RDB持久化,和save指令不同是:Redis服務一般不會阻塞。 Redis進程會執行fork操作創建子進程,RDB持久化由子進程負責,不會阻塞Redis服務進程。 Redis服務的阻塞只發生在fork階段,一般情況時間很短。
    bgsave指令的具體流程如下圖:
    完全掌握Redis持久化:RDB和AOF
    1、執行bgsave指令,Redis程式先判斷目前是否存在正在執行的RDB或AOF子線程,如果存在就是直接結束。
    2、Redis程序執行fork操作建立子執行緒,在fork操作的過程中Redis程序會被阻塞。
    3、Redis程序fork完成後,bgsave指令就結束了,自此Redis程序不會被阻塞,可以回應其他指令。
    4、子進程根據Redis進程的記憶體產生快照文件,並替換原有的RDB文件。
    5.同時傳送訊號給主程序,通知主程序rdb持久化完成,主程序更新相關的統計資料(info Persitence下的rdb_*相關選項)。

4、自動觸發

除了執行以上指令手動觸發以外,Redis內部可以自動觸發RDB持久化。自動觸發的RDB持久化都是採用bgsave的方式,減少Redis進程的阻塞。那麼,在什麼場景下會自動觸發呢?

  1. 在設定檔中設定了save的相關配置,如sava m n,它表示在m秒內資料被修改過n次時,自動觸發bgsave操作。
  2. 當從節點做全量複製時,主節點會自動執行bgsave操作,並且把產生的RDB檔案傳送給從節點。
  3. 執行debug reload指令時,也會自動觸發bgsave操作。
  4. 執行shutdown指令時,如果沒有開啟AOF持久化也會自動觸發bgsave操作。

5、RDB優點

RDB檔案是一個緊湊的二進位壓縮文件,是Redis在某個時間點的全部資料快照。所以使用RDB恢復資料的速度遠比AOF的快,非常適合備份、全量複製、災難復原等場景。

6、RDB缺點

每次進行bgsave操作都要執行fork操作創建子經常,屬於重量級操作,頻繁執行成本過高,所以無法做到即時持久化,或者秒級持久化。

另外,由於Redis版本的不斷迭代,存在不同格式的RDB版本,有可能出現低版本的RDB格式無法相容高版本RDB檔案的問題。

7、dump.rdb中配置RDB

快照週期:記憶體快照雖然可以透過技術人員手動執行SAVEBGSAVE 指令來進行,但生產環境下多數情況都會設定其週期性執行條件。

  • Redis中預設的週期新設定
# 周期性执行条件的设置格式为
save <seconds> <changes>

# 默认的设置为:
save 900 1
save 300 10
save 60 10000

# 以下设置方式为关闭RDB快照功能
save ""</changes></seconds>

以上三個預設資訊設定代表的意義是:

  • 如果900秒内有1条Key信息发生变化,则进行快照;
  • 如果300秒内有10条Key信息发生变化,则进行快照;
  • 如果60秒内有10000条Key信息发生变化,则进行快照。读者可以按照这个规则,根据自己的实际请求压力进行设置调整。
  • 其它相关配置
# 文件名称
dbfilename dump.rdb

# 文件保存路径
dir ./

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查
rdbchecksum yes
  • dbfilename:RDB文件在磁盘上的名称。
  • dir:RDB文件的存储路径。默认设置为“./”,也就是Redis服务的主目录。
  • stop-writes-on-bgsave-error:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现Redis的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为yes,如果要关闭这个特性,指定即使出现快照错误Redis一样允许写操作,则可以将该值更改为no。
  • rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用LZF压缩算法。Redis官方的建议是请保持该选项设置为yes,因为“it’s almost always a win”。
  • rdbchecksum:从RDB快照功能的version 5 版本开始,一个64位的CRC冗余校验编码会被放置在RDB文件的末尾,以便对整个RDB文件的完整性进行验证。这个功能大概会多损失10%左右的性能,但获得了更高的数据可靠性。所以如果您的Redis服务需要追求极致的性能,就可以将这个选项设置为no。

8、 RDB 更深入理解

  • 由于生产环境中我们为Redis开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间Redis服务一般都会收到数据写操作请求。那么如何保证数据一致性呢?
    RDB中的核心思路是Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面Redis主进程会fork一个新的快照进程专门来做这个事情,这样保证了Redis服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。
    举个例子:如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
    完全掌握Redis持久化:RDB和AOF
  • 在进行快照操作的这段时间,如果发生服务崩溃怎么办?
    很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的RDB快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。
  • 可以每秒做一次快照吗?
    对于快照来说,所谓“连拍”就是指连续地做快照。这样一来,快照的间隔时间变得很短,即使某一时刻发生宕机了,因为上一时刻快照刚执行,丢失的数据也不会太多。但是,这其中的快照间隔时间就很关键了。
    如下图所示,我们先在 T0 时刻做了一次快照,然后又在 T0+t 时刻做了一次快照,在这期间,数据块 5 和 9 被修改了。如果在 t 这段时间内,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复。此时,数据块 5 和 9 的修改值因为没有快照记录,就无法恢复了。
    完全掌握Redis持久化:RDB和AOF

针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决

三、AOF持久化

AOF(Append Only File)持久化是把每次写命令追加写入日志中,当需要恢复数据时重新执行AOF文件中的命令就可以了。AOF解决了数据持久化的实时性,也是目前主流的Redis持久化方式。

Redis是“写后”日志,Redis先执行命令,把数据写入内存,然后才记录日志。日志里记录的是Redis收到的每一条命令,这些命令是以文本形式保存。PS: 大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性。

而AOF日志采用写后日志,即先写内存,后写日志
完全掌握Redis持久化:RDB和AOF
为什么采用写后日志?
Redis要求高性能,采用写日志有两方面好处:

  • 避免额外的检查开销:Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis 在使用日志恢复数据时,就可能会出错。
  • 不会阻塞当前的写操作

但这种方式存在潜在风险:

  • 如果命令执行完成,写日志之前宕机了,会丢失数据。
  • 主线程写磁盘压力大,导致写盘慢,阻塞后续操作。

1、如何实现AOF?

AOF日志记录Redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

  • 命令追加 当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。
  • 文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:
    完全掌握Redis持久化:RDB和AOF
  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

2、redis.conf中配置AOF

默认情况下,Redis是没有开启AOF的,可以通过配置redis.conf文件来开启AOF持久化,关于AOF的配置如下:

# appendonly参数开启AOF持久化
appendonly no

# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"

# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./

# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加载aof出错如何处理
aof-load-truncated yes

# 文件重写策略
aof-rewrite-incremental-fsync yes

以下是Redis中关于AOF的主要配置信息:
appendfsync:这个参数项是AOF功能最重要的设置项之一,主要用于设置“真正执行”操作命令向AOF文件中同步的策略。

什么叫“真正执行”呢?还记得Linux操作系统对磁盘设备的操作方式吗? 为了保证操作系统中I/O队列的操作效率,应用程序提交的I/O操作请求一般是被放置在linux Page Cache中的,然后再由Linux操作系统中的策略自行决定正在写到磁盘上的时机。而Redis中有一个fsync()函数,可以将Page Cache中待写的数据真正写入到物理设备上,而缺点是频繁调用这个fsync()函数干预操作系统的既定策略,可能导致I/O卡顿的现象频繁 。

与上节对应,appendfsync参数项可以设置三个值,分别是:always、everysec、no,默认的值为everysec。

no-appendfsync-on-rewrite:always和everysec的设置会使真正的I/O操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的Redis是没法解决的。为了尽量缓解这个情况,Redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的Page Cache(这段时间Redis还在接受客户端的各种写操作命令)。

auto-aof-rewrite-percentage:上文說到在生產環境下,技術人員不可能隨時隨地使用「BGREWRITEAOF」指令去重寫AOF檔。所以更多時候我們需要依賴Redis中對AOF檔的自動重寫策略。 Redis中觸發自動重寫AOF檔的操作提供了兩個設定:
auto-aof-rewrite-percentage表示如果目前AOF檔的大小超過了上次重寫後AOF檔的百分之幾後,就再次開始重寫AOF檔。例如此參數值的預設值為100,意思是如果AOF檔案的大小超過上次AOF檔案重寫後的1倍,就啟動重寫操作。
auto-aof-rewrite-min-size:設定項目表示啟動AOF檔案重寫作業的AOF檔案最小大小。如果AOF檔案大小低於這個值,則不會觸發重寫操作。請注意,auto-aof-rewrite-percentage和auto-aof-rewrite-min-size只是用來控制Redis中自動對AOF檔案進行重寫的情況,如果是技術人員手動調用“BGREWRITEAOF”命令,則不受這兩個限制條件左右。

3、深入理解AOF重寫

AOF會記錄每個寫入指令到AOF文件,隨著時間越來越長,AOF檔案會變得越來越大。如果不加以控制,會對Redis伺服器,甚至對作業系統造成影響,而且AOF檔案越大,資料復原也越慢。為了解決AOF檔案體積膨脹的問題,Redis提供AOF檔案重寫機制來對AOF檔案進行「瘦身」。

圖例解釋AOF重寫
完全掌握Redis持久化:RDB和AOF
#AOF重寫會阻塞嗎?
AOF重寫過程是由後台程序bgrewriteaof來完成的。主執行緒fork出後台的bgrewriteaof子進程,fork會把主執行緒的記憶體拷貝一份給bgrewriteaof子進程,這裡面就包含了資料庫的最新資料。然後,bgrewriteaof子程序就可以在不影響主執行緒的情況下,逐一把拷貝的資料寫成操作,記入重寫日誌。所以aof在重寫時,在fork進程時是會阻塞住主執行緒的。

AOF日誌何時會重寫?
有兩個設定項控制AOF重寫的觸發:
auto-aof-rewrite-min-size:表示執行AOF重寫時檔案的最小大小,預設為64MB。
auto-aof-rewrite-percentage:這個值的計算方式是,當前aof檔案大小和上次重寫後aof檔案大小的差值,再除以上一次重寫後aof文件大小。也就是當前aof檔案比上次重寫後aof檔案的增量大小,和上一次重寫後aof檔案大小的比值。

重寫日誌時,有新資料寫入咋整?
重寫過程總結為:「一個拷貝,兩個日誌」。在fork出子程序時的拷貝,以及在重寫時,如果有新資料寫入,主執行緒就會將指令記錄到兩個aof日誌記憶體緩衝區。如果AOF寫回策略配置的是always,則直接將命令寫回舊的日誌文件,並且保存一份命令至AOF重寫緩衝區,這些操作對新的日誌檔案是不存在影響的。 (舊的日誌檔案:主執行緒使用的日誌文件,新的日誌檔案:bgrewriteaof進程使用的日誌檔案)

而在bgrewriteaof子進程完成日誌檔案的重寫操作後,會提示主執行緒已經完成重寫操作,主執行緒會將AOF重寫緩衝中的指令追加到新的日誌檔案後面。這時候在高併發的情況下,AOF重寫緩衝區累積可能會很大,這樣就會造成阻塞,Redis後來透過Linux管道技術讓aof重寫期間就能同時進行回放,這樣aof重寫結束後只需回放少量剩餘的數據即可。最後透過修改檔名的方式,確保檔案切換的原子性。

在AOF重寫日誌期間發生宕機的話,因為日誌檔案還沒切換,所以恢復資料時,使用的還是舊的日誌檔案。

總結操作:

  • 主執行緒fork出子程序重寫aof日誌
  • 子程序重寫日誌完成後,主執行緒追加aof日誌緩衝
  • 取代日誌檔案

#溫馨提示

##這裡的行程與執行緒的概念有點混亂。因為後台的bgreweiteaof進程就只有一個執行緒在操作,而主執行緒是Redis的操作進程,也是單獨一個執行緒。這裡想表達的是Redis主程序在fork出一個後台程序之後,後台程序的操作和主程序是沒有任何關聯的,也不會阻塞主執行緒

完全掌握Redis持久化:RDB和AOF
主執行緒fork出子程序是如何複製記憶體資料的?
fork採用作業系統提供的寫入時複製(copy on write)機制,就是為了避免一次性拷貝大量記憶體資料造成子程序​​阻塞。 fork子程序時,子程序時會拷貝父進程的頁表,即虛實映射關係(虛擬記憶體和實體記憶體的映射索引表),而不會拷貝實體記憶體。這個拷貝會消耗大量cpu資源,並且拷貝完成前會阻塞主線程,阻塞時間取決於記憶體中的資料量,資料量越大,則記憶體頁表越大。拷貝完成後,父子程序使用相同的記憶體位址空間。

但主程序是可以有資料寫入的,這時候就會拷貝物理記憶體中的資料。如下圖(進程1看做是主進程,進程2看做是子進程):
完全掌握Redis持久化:RDB和AOF
在主進程有資料寫入時,而這個資料剛好在頁c中,作業系統會建立這個頁面的副本(頁c的副本),即拷貝當前頁的物理數據,將其映射到主進程中,而子進程還是使用原來的頁c。

在重寫日誌整個過程時,主執行緒有哪些地方會被阻塞?

  • fork子程序時,需要拷貝虛擬頁表,會對主執行緒阻塞。
  • 主程序有bigkey寫入時,作業系統會建立頁面的副本,並拷貝原有的數據,會對主執行緒阻塞。
  • 子程序重寫日誌完成後,主程序追加aof重寫緩衝區時可能會對主執行緒阻塞。

為什麼AOF重寫不重複使用原始AOF日誌?

  • 父子程式寫同一個檔案會產生競爭問題,影響父行程的效能。
  • 如果AOF重寫過程中失敗了,相當於污染了原本的AOF文件,無法做恢復資料使用。

三、RDB和AOF混合方式(4.0版本)

Redis 4.0 中提出了一個混合使用 AOF 日誌和記憶體快照的方法。簡單來說,記憶體快照以一定的頻率執行,在兩個快照之間,使用 AOF 日誌記錄這段期間的所有命令操作。

這樣一來,快照就不用很頻繁地執行,這就避免了頻繁 fork 對主執行緒的影響。而且,AOF 日誌也只用記錄兩個快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現文件過大的情況了,也可以避免重寫開銷。

如下圖所示,T1 和T2 時刻的修改,用AOF 日誌記錄,等到第二次做全量快照時,就可以清空AOF 日誌,因為此時的修改都已經記錄到快照中了,恢復時就不再用日誌了。
完全掌握Redis持久化:RDB和AOF
這個方法既能享受到 RDB 檔案快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢, 實際環境中用的很多。

四、從持久化中恢復資料

資料的備份、持久化做完了,我們要如何從這些持久化檔案中恢復資料呢?如果一台伺服器上有既有RDB文件,又有AOF文件,那該載入誰呢?

其實想要從這些檔案中恢復數據,只需要重新啟動Redis。我們還是透過圖來了解這個流程:
完全掌握Redis持久化:RDB和AOF

  • redis重啟時判斷是否開啟aof,如果開啟了aof,那麼就優先載入aof檔案;
  • 如果aof存在,那麼就去加載aof文件,加載成功的話redis重啟成功,如果aof文件加載失敗,那麼會打印日誌表示啟動失敗,此時可以去修復aof文件後重新啟動;
  • 若aof文件不存在,那麼redis就會轉而去加載rdb文件,如果rdb文件不存在,redis直接啟動成功;
  • 如果rdb文件存在就會去加載rdb文件恢復數據,如加載失敗則列印日誌提示啟動失敗,如載入成功,那麼redis重啟成功,且使用rdb檔恢復資料;

那為什麼會優先載入AOF呢?因為AOF保存的資料更完整,透過上面的分析我們知道AOF基本上最多損失1s的資料。

推薦學習:Redis影片教學

以上是完全掌握Redis持久化:RDB和AOF的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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