這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於持久化的相關問題,包括了為什麼需要持久化、RDB持久化、AOF持久化等等內容,下面一起來看一下,希望對大家有幫助。
推薦學習:Redis影片教學
Redis對資料的操作都是基於記憶體的,當遇到了進程退出、伺服器當機等意外情況,如果沒有持久化機制,那麼Redis中的資料將會遺失無法恢復。有了持久化機制,Redis在下次重啟時可以利用先前持久化的檔案進行資料恢復。 Redis支援的兩種持久化機制:
RDB:把目前資料產生快照保存在硬碟上。
AOF:記錄每次對資料的操作到硬碟上。
在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,它還原時是將快照檔案直接讀到內存裡。 RDB(Redis DataBase)持久化是把目前Redis中全部資料產生快照保存在硬碟上。 RDB持久化可以手動觸發,也可以自動觸發。
redis會單獨建立(fork)一個子程序來進行持久化,會先將資料寫入到暫存器中,待持久化過程都結束了,再用這個暫存檔案取代上次持久化好了的文件。整個過程中,主進程是不進行任何IO操作的,這確保了極高的效能。如果需要進行大規模資料的恢復,且對資料的恢復完整性不是非常敏感,那麼RDB方式要比AOF方式更加的高效。 RDB的缺點是最後一次持久化的資料可能會遺失。
save
和bgsave
指令都可以手動觸發RDB持久化。
save
save
指令會手動觸發RDB持久化,但save
指令會阻塞Redis服務,直到RDB持久化完成。當Redis服務儲存大量資料時,會造成較長時間的阻塞,不建議使用。 bgsave
bgsave
指令也會手動觸發RDB持久化,和save
指令不同是:Redis服務一般不會阻塞。 Redis進程會執行fork操作創建子進程,RDB持久化由子進程負責,不會阻塞Redis服務進程。 Redis服務的阻塞只發生在fork階段,一般情況時間很短。 bgsave
指令的具體流程如下圖:bgsave
指令,Redis程式先判斷目前是否存在正在執行的RDB或AOF子線程,如果存在就是直接結束。 bgsave
指令就結束了,自此Redis程序不會被阻塞,可以回應其他指令。 除了執行以上指令手動觸發以外,Redis內部可以自動觸發RDB持久化。自動觸發的RDB持久化都是採用bgsave
的方式,減少Redis進程的阻塞。那麼,在什麼場景下會自動觸發呢?
save
的相關配置,如sava m n
,它表示在m秒內資料被修改過n次時,自動觸發bgsave
操作。 bgsave
操作,並且把產生的RDB檔案傳送給從節點。 debug reload
指令時,也會自動觸發bgsave
操作。 shutdown
指令時,如果沒有開啟AOF持久化也會自動觸發bgsave
操作。 RDB檔案是一個緊湊的二進位壓縮文件,是Redis在某個時間點的全部資料快照。所以使用RDB恢復資料的速度遠比AOF的快,非常適合備份、全量複製、災難復原等場景。
每次進行bgsave
操作都要執行fork操作創建子經常,屬於重量級操作,頻繁執行成本過高,所以無法做到即時持久化,或者秒級持久化。
另外,由於Redis版本的不斷迭代,存在不同格式的RDB版本,有可能出現低版本的RDB格式無法相容高版本RDB檔案的問題。
快照週期:記憶體快照雖然可以透過技術人員手動執行SAVE
或BGSAVE
指令來進行,但生產環境下多數情況都會設定其週期性執行條件。
# 周期性执行条件的设置格式为 save <seconds> <changes> # 默认的设置为: save 900 1 save 300 10 save 60 10000 # 以下设置方式为关闭RDB快照功能 save ""</changes></seconds>
以上三個預設資訊設定代表的意義是:
# 文件名称 dbfilename dump.rdb # 文件保存路径 dir ./ # 如果持久化出错,主进程是否停止写入 stop-writes-on-bgsave-error yes # 是否压缩 rdbcompression yes # 导入时是否检查 rdbchecksum yes
bgsave
子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave
子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决
AOF(Append Only File)持久化是把每次写命令追加写入日志中,当需要恢复数据时重新执行AOF文件中的命令就可以了。AOF解决了数据持久化的实时性,也是目前主流的Redis持久化方式。
Redis是“写后”日志,Redis先执行命令,把数据写入内存,然后才记录日志。日志里记录的是Redis收到的每一条命令,这些命令是以文本形式保存。PS: 大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性。
而AOF日志采用写后日志,即先写内存,后写日志。
为什么采用写后日志?
Redis要求高性能,采用写日志有两方面好处:
但这种方式存在潜在风险:
AOF日志记录Redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。
默认情况下,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
”命令,則不受這兩個限制條件左右。
AOF會記錄每個寫入指令到AOF文件,隨著時間越來越長,AOF檔案會變得越來越大。如果不加以控制,會對Redis伺服器,甚至對作業系統造成影響,而且AOF檔案越大,資料復原也越慢。為了解決AOF檔案體積膨脹的問題,Redis提供AOF檔案重寫機制來對AOF檔案進行「瘦身」。
圖例解釋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重寫日誌期間發生宕機的話,因為日誌檔案還沒切換,所以恢復資料時,使用的還是舊的日誌檔案。
總結操作:
#溫馨提示
##這裡的行程與執行緒的概念有點混亂。因為後台的bgreweiteaof進程就只有一個執行緒在操作,而主執行緒是Redis的操作進程,也是單獨一個執行緒。這裡想表達的是Redis主程序在fork出一個後台程序之後,後台程序的操作和主程序是沒有任何關聯的,也不會阻塞主執行緒
主執行緒fork出子程序是如何複製記憶體資料的?
fork採用作業系統提供的寫入時複製(copy on write)機制,就是為了避免一次性拷貝大量記憶體資料造成子程序阻塞。 fork子程序時,子程序時會拷貝父進程的頁表,即虛實映射關係(虛擬記憶體和實體記憶體的映射索引表),而不會拷貝實體記憶體。這個拷貝會消耗大量cpu資源,並且拷貝完成前會阻塞主線程,阻塞時間取決於記憶體中的資料量,資料量越大,則記憶體頁表越大。拷貝完成後,父子程序使用相同的記憶體位址空間。
但主程序是可以有資料寫入的,這時候就會拷貝物理記憶體中的資料。如下圖(進程1看做是主進程,進程2看做是子進程):
在主進程有資料寫入時,而這個資料剛好在頁c中,作業系統會建立這個頁面的副本(頁c的副本),即拷貝當前頁的物理數據,將其映射到主進程中,而子進程還是使用原來的頁c。
在重寫日誌整個過程時,主執行緒有哪些地方會被阻塞?
為什麼AOF重寫不重複使用原始AOF日誌?
Redis 4.0 中提出了一個混合使用 AOF 日誌和記憶體快照的方法。簡單來說,記憶體快照以一定的頻率執行,在兩個快照之間,使用 AOF 日誌記錄這段期間的所有命令操作。
這樣一來,快照就不用很頻繁地執行,這就避免了頻繁 fork 對主執行緒的影響。而且,AOF 日誌也只用記錄兩個快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現文件過大的情況了,也可以避免重寫開銷。
如下圖所示,T1 和T2 時刻的修改,用AOF 日誌記錄,等到第二次做全量快照時,就可以清空AOF 日誌,因為此時的修改都已經記錄到快照中了,恢復時就不再用日誌了。
這個方法既能享受到 RDB 檔案快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢, 實際環境中用的很多。
資料的備份、持久化做完了,我們要如何從這些持久化檔案中恢復資料呢?如果一台伺服器上有既有RDB文件,又有AOF文件,那該載入誰呢?
其實想要從這些檔案中恢復數據,只需要重新啟動Redis。我們還是透過圖來了解這個流程:
那為什麼會優先載入AOF呢?因為AOF保存的資料更完整,透過上面的分析我們知道AOF基本上最多損失1s的資料。
推薦學習:Redis影片教學
以上是完全掌握Redis持久化:RDB和AOF的詳細內容。更多資訊請關注PHP中文網其他相關文章!