詳解Redis持久化(持久化高危漏洞利用與多種對抗方案、RDB、AOF、同步手動持久化、非同步手動非阻塞持久化、備份檢測、備份修復、壓縮原理、雙備份模式、純快取模式)

小松聊PHP进阶發表於2024-05-12

謹防持久化+未授權訪問漏洞入侵伺服器

CVE編號找不到,CNVD有一個:CNVD-2015-07557(國家資訊保安漏洞共享平臺漏洞編號)。
這是我之前寫過的文章,漏洞成因、影響範圍、POC與對抗方案有詳解:
謹防利用Redis未授權訪問漏洞入侵伺服器

RDB(Redis Database、全量儲存,預設方式)

  • 極簡概括:透過符合單位時間資料被修改的量作為觸發,或手動觸發,把某一時刻的全部資料寫到磁碟。
  • 現象:編譯安裝完Redis後啟動,預設會在根目錄(這預設配置地址得吐槽)下生成一個dump.rdb檔案。
  • 會被自動觸發的情形:
    • 配置設定。
    • 關閉Redis服務。
    • Flashall命令。
  • 相關配置:從6.2.0開始有差異:
6.2.0之前
save 900 1        15分鐘至少有1個值被改動,這是避免備份資料與記憶體資料長期不一致的兜底策略。
save 300 10       5分鐘至少有10個值被改動,中庸策略。
save 60 10000     1分鐘至少有1萬個值被改動,避免備份資料與記憶體資料短時間大量不一致的策略。

6.2.0之後,這意味著某些場景,需要改變配置。
save 3600 1
save 300 100
save 60 10000

這種東西怎麼配,若資料要緊,時間就配置的少一點,若要求效能,對資料丟失有容忍度,那就按照預設配置,或加大時間配置。


若要修改rdb檔案存放位置和名稱
dir 自定義路徑
dbfilename 自定義檔名.rdb


其它配置:
stop-writes-on-bgsave-error yes
用於在BGSAVE操作出現錯誤時Redis的行為。當這個配置選項被設定為yes時,如果後臺儲存操作BGSAVE失敗,Redis將停止接受寫操作,以防止資料丟失。這樣做可以確保資料的完整性,因為在無法完成後臺儲存操作時,寫入新資料可能會導致資料不一致或丟失。
當stop-writes-on-bgsave-error被設定為no時,Redis在後臺儲存操作失敗時仍然會繼續接受寫操作。這可能會導致資料不一致或丟失,但也可以確保寫操作的連續性。

rdbcompression yes
指定Redis在執行RDB時是否啟用LZF演算法壓縮。

rdbchecksum yes
用於指定在執行RDB(Redis Database)持久化時是否對資料進行校驗和檢查,會有10%的效能消耗。

rdb-del-sync-files no
在沒有永續性的例項中刪除複製使用的RDB檔案。
  • redis/bin/redis-check-rdb,可檢測rdb檔案是否存壞,並修復。

RDB同步持久化與非同步非阻塞持久化

  • save(不推薦用):執行期間會阻塞其它命令(意味著臨時停止服務)直到執行完畢,不適合大量請求場景,由於是直接執行,效能比bgsave略高。
  • bgsave:會fork一個子程序去處理備份,非同步執行期間不會阻塞其它命令,但是若資料資源相當龐大,仍舊會拉低Redis的效能。
  • lastsave:使用lastsave命令,可以檢視最後一次執行RDB的時間,對於Redis運維還是有幫助的。
  • 注意:在持久化時,需要考慮資料量的大小,與磁碟大小,避免記憶體或者磁碟不足讓服務夯住的情況發生。
  • fork: 在Unix/Linux系統中用來建立一個新的子程序的操作。在呼叫fork() 後,作業系統會複製當前程序的所有內容(包括記憶體資料、堆疊等)到新的子程序中,從而建立一個完全獨立的子程序,而子程序擁有獨立的記憶體空間和程序 ID。

RDB恢復資料

  • 注意:由於Redis RDB是全量備份,所以不會像MySQL binlog那種,可以恢復部分資料。也不是像MySQL redo log那種接近實時的備份,RDB備份有間隙,所以就算透過檔案恢復,也可能丟失部分資料。
  • 如果誤刪了資料:其它節點(如MySQL)又沒有備份,可以利用這個間隙,立即把rdb檔案備份,防止觸發redis同步功能覆蓋原資料,把之前持久化的資料整沒了,然後停止Redis(一定要在Redis停止之前備份,利用Redis命令停止Redis也會觸發一次RDB持久化,利用作業系統命令幹掉程序則不會),接著把持久化名字改過來,隨後啟動Redis。
  • 預設處理方式:Redis在啟動時會讀取配置,所以RDB檔案不需要手動匯入,Redis會直接讀取裡面的資料。
  • 優點:由於RDB檔案具有緊湊的單一檔案,所以可以方便的被異地備份,常用的容災手段。

禁用RDB

修改配置檔案:清除未被註釋的save項,改為一個save ""

AOF(Append Only File、追加寫,增量儲存)

  • 極簡概括:和RDB是並列關係,將Redis伺服器執行的寫命令追加到一個檔案的末尾,由於備份和執行不是實時的原子性操作,所以仍舊有丟失的可能(Redis存在重點是快速用於元件間通訊,而不是持久化儲存)。
  • 誤區:這裡的only單詞很多餘,有only不代表RDB模式不能被同時啟用。
  • 存在意義:
    1. 畢竟是主流的資料庫,所以要在持久化上利用多備份機制做到位。
    2. AOF彌補了RDB備份時間間隙(最後一次備份~當前時間未儲存)的問題。
    3. AOF彌補了RDB備份時產生大量磁碟IO的情況發生。
  • 配置
找到redis.conf
appendonly no 改為yes

配置持久化模式
appendfsync everysec/always/no
always:每次寫操作,都進行一次追加,最安全但效能最差。
everysec:預設值,每秒進行一次追加,最多丟失一秒鐘的資料。
no:讓作業系統來處理資料同步,由作業系統自行決定何時將資料重新整理到磁碟。
三者從上到下效能依次提升,高可用依次降低。

配置儲存路徑,Redis7新配置,若低版本沒有,就和RDB目錄一個位置
appenddirname 'appendonlydir'

配置檔名稱
appendfilename "appendonly.aof"

Redis7針對aof檔案做了拆分
appendonly.aof.base:基本檔案,在壓縮檔案時,會將壓縮後的最小指令集保留在此當中。
appendonly.aof.incr:增量檔案,該檔案儲存對基本AOF檔案進行的增量更改。
appendonly.aof.manifest:清單檔案,該檔案可能與管理和跟蹤AOF檔案的狀態或後設資料。


備份壓縮相關配置,下文有講
上次重寫之後的增長百分比,這裡預設為100
auto-aof-rewrite-percentage 100

表示aof檔案超過64MB。
auto-aof-rewrite-min-size 64mb


雜項配置
no-appendfsync-on-rewrite no
這個配置指示Redis如何將資料寫入磁碟,設定為no表示Redis在重寫時不會改變其關於appendfsync設定的行為。

AOF恢復資料

  • 常規情況下:aof備份檔案恢復資料是自動的,不需要手動操作。
  • 若手動備份檔案的方式恢復:若對aof檔案做了更名用於多檔案備份,恢復時需要改為配置檔案中的名稱,然後重啟,即可恢復aof檔案中的資料。
  • 注意:由於flushall也是寫操作,所以執行它有也會同步aof檔案,但是由於aof是增量備份,所以在aof檔案中刪除flushall字眼,然後重啟,就能恢復資料。

AOF破損修復與檢測

redis-check-aof 是專門用於檢測與修復aof檔案的工具,在redis-cli同級目錄。

檢測,看到AOF is valid說明檔案無問題:
./redis-check-aof ../appendonly.aof 
AOF analyzed: size=131, ok_up_to=131, diff=0
AOF is valid


但是由於斷電,程序被強制終止,或其它原因導致的檔案損壞,可以新增 --fix 去修復。

模擬檔案損壞,編輯檔案後隨意載入點東西
vim ../appendonly.aof

重啟redis服務,發現redis服務起不來了(配置有服務,下文有講)
service redis restart

修復,看見Successfully字眼,說明修復成功,若是redis7,請操作appendonly.aof.incr增量檔案
./redis-check-aof --fix ../appendonly.aof
0x              83: Expected prefix '*', got: 'a'
AOF analyzed: size=139, ok_up_to=131, diff=8
This will shrink the AOF from 139 bytes, with 8 bytes, to 131 bytes
Continue? [y/N]: y
Successfully truncated AOF

啟動redis
service redis start

AOF備份壓縮

  • 簡單概括:由於寫命令的增加,AOF檔案越來越大,恢復速度也會隨之增加,因此Redis新增了重寫機制,當AOF超過所設定的最大值時,Redis就會自動啟動檔案壓縮,只保留可以恢復資料的最小有效指令集,或使用bgrewriteaof來進行壓縮。
  • 核心原理:原先set a 1,再執行set a 2,一直到set a 10,初始的aof記錄方式是記錄10條備份,而壓縮只需要記錄1條set a 10,這樣用於減小檔案,也用於快速恢復資料,這就是最小有效的指令集。但是一開始就這樣做,意味著時間換空間(更多的計算),會降低aof的效能。
  • 自動觸發(需同時滿足兩個配置檔案中的條件):
    • auto-aof-rewrite-percentage 100:上次重寫之後的增長百分比,這裡預設為100。
    • auto-aof-rewrite-min-size 64mb:表示aof檔案超過64MB。
  • 手動觸發:命令列執行bgrewriteaof命令。

雙備份模式

上文有講,預設開啟的RDB和手動開啟AOF,不會互相影響,兩者是並列關係。
但是有備份粒度更小的AOF,redis啟動時,就不會載入RDB。除非在配置檔案中配置aof-use-rdb-preamble yes(實測這個配置yes或no不影響AOF+RDB的同時備份,版本5.0.9)。
這個配置值的意義是:用於控制是否在AOF檔案的開頭使用RDB檔案的格式。當設定為yes時,Redis在AOF檔案的開頭會新增一個RDB檔案的內容,以便在重啟時快速載入資料,提高啟動速度和恢復能力。

純快取模式

  • RDB:配置save ‘’
  • AOF:配置appendonly no
  • 注意:經過實測,仍舊可以使用bgsave或save生成rdb檔案,或bgrewriteaof生成aof檔案。

擴充套件,Redis服務編寫

或許你們好奇redis的啟動、關閉、重啟、狀態檢視,上文用的service命令,其實是利用bash shell做的,如下:

touch /etc/init.d/redis
chmod +x /etc/init.d/redis
chkconfig redis on,或 systemctl enable redis.service
vim /etc/init.d/redis


#! /bin/bash
REDISPORT=6379
EXEC=/usr/local/redis/bin/redis-server
REDIS_CLI=/usr/local/redis/bin/redis-cli
   
PIDFILE=/var/run/redis.pid
CONF="/usr/local/redis/etc/redis.conf"
   
case "$1" in
    start)
        if [ -f "$PIDFILE" ]; then
            echo "$PIDFILE PID檔案已存在,程序已在執行或崩潰"
        else
            echo -n "正在啟動Redis..."
            $EXEC $CONF
            if [ "$?"="0" ]; then
                echo "啟動成功"
            else
                echo "啟動失敗"
            fi
        fi
        ;;
    stop)
        if [ ! -f "$PIDFILE" ]; then
            echo "$PIDFILE PID檔案不存在,沒有被執行"
        else
            PID=$(cat $PIDFILE)
            echo "正在關閉Redis"
            $REDIS_CLI -p $REDISPORT shutdown
            if [ "$?"="0" ]; then
                echo "關閉成功"
            else
                echo "關閉失敗"
            fi
        fi
        ;;
    restart)
        ${0} stop
        ${0} start
        ;;
    kill)
        echo "強制終止Redis程序"
        killall redis-server
        if [ "$?"="0" ]; then
            echo "終止成功"
        else
            echo "終止失敗"
        fi
        ;;
    status)
        if [ -f "$PIDFILE" ]; then
            echo "Redis正在執行"
        else
            echo "Redis已停止"
        fi
        ;;
  *)
    echo "命令出錯,只能執行service redis start、stop、restart、status、kill" >&2
        exit 1
esac

相關文章