寫在前面
參考文章:
- https://blog.csdn.net/qq_41453285/article/details/103298285
1.Redis持久化
爲什麼要持久化?爲了重啓恢復或者故障恢復。
Redis提供了兩種持久化方案,分別是RDB和AOF。
1.1 RDB
1.1.1 概述
定時對內存進行快照存儲,默認保存爲dump.rdb文件,快照完成後替換舊rdb文件。
1.1.2 RDB優點
- 當需要快照時,主進程僅fork一個進程,然後由子進程執行快照,因此不會影響主進程處理用戶請求。
- 當需要恢復大數據集時,相比AOF,RDB的恢復速度更快。
1.1.3 RDB缺點
- RDB不能很頻繁的執行,因爲數據集很大時,快照是比較耗時的,即使通過fork子進程,但是仍然會佔用系統資源的,所以一般會設置幾分鐘一次,因此如果Redis發生宕機等異常時,可能會丟失幾分鐘的數據。
1.1.4 配置方法
兩個參數:時間和寫操作總數,比如:
save 60 1000
表示:60秒內至少1000次變更則觸發快照。
分析:根據以上配置,無論寫操作有多頻繁,最快也是60秒進行一次快照,如果60秒內不超過1000次則不會進行,也就是說如果每分鐘的寫操作都不超過1000次,那麼永遠不會進行快照,爲了防止該情況,Redis RDB的默認配置如下:
save 900 1 #900秒內至少有1個key被更改就執行快照
save 300 10 #300內描述至少有10個key被更改就執行快照
save 60 10000 #60秒內至少有10000個key被更改就執行快照
通過配置save 900 1保證15分鐘內如果有寫操作則最少進行一次快照。
1.2 AOF
1.2.1 概述
append only file,即記錄每次寫操作到文件中,當需要恢復時,重新執行所有寫操作來恢復現場,恢復優先級高於RDB;並且Redis還支持後臺重寫文件(bgrewrite),即合併一些命令使得文件不至於過大,節約硬盤空間,比如有兩次寫操作記錄到文件中:
set a 1
expire a 1000
經過重寫後:
setex a 1 1000
1.2.2 持久化流程
Redis的主進程就是一個事件循環(eventLoop),這個循環中處理兩個事件,分爲是文件事件和事件時間;文件事件負責處理客戶端的命令,而時間事件則負責執行像serverCron函數這樣需要定時運行的函數。
文件事件處理過程中,如果執行了寫命令,則在執行完命令後,將命令追加到aof_buf緩衝區。
每次結束一個事件循環之前,它都會調用flushAppendOnlyFile函數,考慮是否需要將aof_buf緩衝區中的內容寫入和保存到AOF文件裏面。
以上過程可以用以下僞代碼表示:
def eventLoop():
while True:
#處理文件事件
processFileEvents()
# 處理時間事件
processTimeEvents()
# 考慮是否要將 aof_buf 中的內容寫入和保存到 AOF 文件裏面
flushAppendOnlyFile()
什麼是寫入(wirte)和保存(save)?
寫入就是調用操作系統的文件寫入方法,試圖將緩衝區內容寫到文件中,但是因爲操作系統有寫緩衝區,所以Redis進程調用成功,並不代表持久化成功(當寫緩衝區滿了或者操作系統配置了定時同步策略時到時間了,操作系統會將寫緩衝區同步到文件中)。
保存,又稱爲同步,操作系統提供同步接口以使得進程可以強制要求將寫緩衝區同步到文件中。
flushAppendOnlyFile函數
根據【appendfsync】參數執行不同的邏輯,appendfsync有三種,分別是:
AOF_FSYNC_NO :將緩衝區內容寫入文件,但是不進行保存/同步,何時同步由操作系統決定。
AOF_FSYNC_EVERYSEC :將緩衝區內容寫入文件,如果上次同步距離現在已超過一秒鐘,則再次進行同步,這個同步操作由子線程執行。
AOF_FSYNC_ALWAYS :將緩衝區內容寫入並同步到AOF文件。
1.2.2.1 AOF_FSYNC_NO
在這種模式下,都由主進程執行,save 只會在以下情況中被執行。
- redis 關閉前
- AOF 功能被關閉
- 系統寫緩存被刷新(緩存已經被寫滿,或者定期保存操作被執行)。
總結:都是這三種情況下都是都會引起redis主進程阻塞。
1.2.2.2 AOF_FSYNC_EVERYSEC
在這種情況下,save原則上每隔一秒鐘都會執行一次,save操作是由子進程執行,所以不會引起服務器主進程阻塞。
注意:實際和原則略有不同,在這種模式下對fsync 或者fdatasync 的調用並不是每秒執行一次,它和調用flushApeendOnlyFile函數時redis 所處的狀態有關。
每當flushAppendOnlyFile函數被調用時,可能會出現以下四種情況:
子進程正在執行save:
1.這個save的執行時間未超過2秒,那麼程序直接返回,並不執行write 或者新的save。
2.這個save的執行時間已超過2秒,那麼執行write,但不執行新的save。因爲write的寫入必須等待子進程先完成save,因此這裏write 會阻塞,即主進程會在這時阻塞,這是爲了保護數據,因爲如果主進程繼續大量執行寫操作,而這些寫操作很可能是無法被持久化的。
子進程沒有在執行save:
3.上次執行成功save距今不超過1秒,那麼程序執行write,但不執行save,因爲這時還沒到save的時間。
4.上次執行成功save距今已超過1秒,那麼程序執行write 和save。
總結:如果程序宕機,則那麼最多丟失2秒的數據。
1.2.2.3 AOF_FSYNC_ALWAYS
在這種模式下,每次執行完一個命令之後,write和save都會被執行。
總結:都由主進程執行的,所以在save 執行期間,主進程會被阻塞,不能處理新的命令請求。
1.2.2.3 AOF 三種同步模式對性能和安全性的影響
-
AOF_FSYNC_NO:這種模式下,Redis主進程在每個事件循環都將緩衝區寫入文件,至於何時同步則由操作系統決定,這種模式效率最高,但是可能丟失大量數據。
-
AOF_FSYNC_EVERYSEC:這種模式下,Redis主進程在每個事件循環都將緩衝區寫入文件,並每隔一秒纔在子線程中執行同步,性能上這種模式足夠快,而且最多丟失2秒的命令數據。
-
AOF_FSYNC_ALWAYS:這種模式下,Redis主進程在每個事件循環都將緩衝區寫入到AOF文件,並且主動同步AOF文件,性能上是最慢的,但是是最安全的,最多隻會丟失一個事件循環中的命令數據。
1.2.3 重寫流程
-
Redis運行期間,所有寫操作都依次被追加到AOF文件中,當文件膨脹到配置的rewrite時則進行重寫,主進程fork一個子進程。
-
主進程繼續接受讀寫命令,但是寫命令在rewrite期間不會被追加到AOF文件,而是緩存到【aof_rewrite_buf_block】緩衝區中。
-
子進程執行rewrite操作,執行成功後替換AOF文件,並通知主進程rewrite完成。
-
主進程收到子進程rewrite完成通知後,將【aof_rewrite_buf_block】緩衝區中的寫操作追加到AOF文件中,然後重新開始第1步。
1.2.4 AOF優點
-
數據更安全,因爲寫操作會被依次追加到文件中,相比RDB可能幾分鐘甚至更久執行一次,AOF策略丟失數據的可能性大大降低。
-
AOF文件中寫操作命令有序,非常易讀,便於分析。
-
恢復更靈活,比如不小心執行了FLUSHALL導致內存數據丟失,如果此時AOF文件沒有被重寫,則關閉Redis並刪除最後的FLUSHALL命令,然後重寫Redis即可恢復。
1.2.5 AOF缺點
-
RDB文件僅存儲內存中的key value,並且可進行壓縮,而AOF需存儲操作命令,因此AOF文件常常比RDB文件大的多。
-
AOF相比RDB更多讓主進程阻塞,寫入和同步AOF文件都需要等待IO,而RDB僅fork子進程時讓主進程阻塞。
1.2.6 AOF_FSYNC_EVERYSEC超時日誌
Asynchronous AOF fsync is taking too long(disk is busy?).writing the AOF buffer without waiting for fsync to complete. this may slow down Redis.
意思是:異步AOF文件同步耗時很長,磁盤很繁忙麼?
在不等待fsync完成的情況下寫入AOF緩衝區,這有可能降低Redis的性能。
異步AOF文件同步指的就是【AOF_FSYNC_EVERYSEC】模式,因爲這個模式是後臺子線程執行fsync操作的。
後面的意思是:文件事件處理方法繼續將寫命令追加到aof_buf緩衝區,但是write將被阻塞,所以會減速Redis
1.2.7 配置方法
-
打開AOF: appendonly yes
-
AOF同步策略:
appendfsync alway #每次(主動)同步
或者
appendfsync everysec #每秒(主動)同步
或者
appendfsync no #不(主動)同步
- AOF文件重寫配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
當文件增大一倍而且大於64mb時,觸發rewrite。