刪庫到跑路?還得看這篇Redis資料庫持久化與企業容災備份恢復實戰指南

WeiyiGeek發表於2022-04-14

本章目錄

0x00 資料持久化

  • 1.RDB 方式
  • 2.AOF 方式
  • 如何抉擇 RDB OR AOF?

0x01 備份容災
一、備份

  • 1.手動備份redis資料庫
  • 2.遷移Redis指定db-資料庫
  • 3.Redis叢集資料備份與遷移

二、恢復

  • 1.系統Redis使用者被刪除後配置資料恢復流程
  • 2.Kubernetes中單例項異常資料遷移恢復實踐
  • 3.當Redis叢集中出現從節點slave,fail,noaddr問題進行處理恢復流程。

前置知識學習補充
Redis資料庫基礎入門介紹與安裝 - https://blog.weiyigeek.top/2019/4-17-49.html

Redis資料庫基礎資料型別介紹與使用 - https://blog.weiyigeek.top/2020/5-17-50.html

Redis基礎運維之原理介紹和主從配置 - https://blog.weiyigeek.top/2019/4-17-97.html

Redis基礎運維之哨兵和叢集安裝配置 - https://blog.weiyigeek.top/2019/4-17-576.html

Redis基礎運維之在K8S中的安裝與配置 - https://blog.weiyigeek.top/2019/4-17-524.html

Redis資料庫效能測試及優化配置 - https://blog.weiyigeek.top/2019/4-17-527.html

Redis資料庫客戶端操作實踐及入坑出坑 - https://blog.weiyigeek.top/2019/4-17-577.html


0x00 資料持久化

描述: Redis 是將資料儲存在記憶體之中所以其讀寫效率非常高,但是往往事物都不是那麼美好,當由於某些不可抗力導致機器當機、redis服務停止此時您在記憶體中的資料將完全丟失;

所以為了使 Redis 在異常重啟後仍能保證資料不丟失, 我們就需要對其進行設定持久化儲存,使其將記憶體的資料通過某種方式存入磁碟中,當Redis服務端重啟後便會從該磁碟中進行讀取資料進而恢復Redis中的資料, 所以Redis資料持久化是容災恢復必備條件;


Redis支援兩種持久化方式:

  • (1) RDB 持久化(預設支援): 該機制是指在指定的時間間隔內將記憶體中資料集寫入到磁碟;
  • (2) AOF 持久化: 該機制將以日誌的形式記錄伺服器所處理的每一個寫操作,同時在Redis伺服器啟動之初會讀取該檔案來重新構建資料庫,以保證啟動後資料庫中的資料完整;
  • (3) 無持久化: 將 Redis 作為一個臨時快取,並將資料存放到memcached之中;

Tips: 為保證資料安全性,我們可以設定 Redis 同時使用RDB和AOF持久化方式,來保證重啟後Redis伺服器中的資料完整;


1.RDB 方式

描述: Redis 將某一時刻的快照(備份的資料庫資料)儲存成一種稱為RDB格式的檔案中,這種格式是經過壓縮的二進位制檔案,資料庫的儲存和恢復檔案如下圖所示。

WeiyiGeek.rdb資料


儲存RDB資料的兩種方式:

  • 1、save命令:save命令會阻塞redis伺服器的程式,直到RDB檔案建立完,在該期間redis不能處理任何的命令請求,這就是save命令最大的缺陷。
  • 2、bgsave命令:與save命令不同的是 bgsave在生成RDB檔案時,會派生出一個子程式,子程式負責建立RDB檔案,在此期間,主程式和子程式是同時存在的,因此不會阻塞redis伺服器程式。 (推薦方式)

說明:可用lastsave命令檢視生成RDB檔案是否成功


優勢

    1. 採用子執行緒建立RDB檔案(dump.rdb),不會對redis伺服器效能造成大的影響( 效能最大化);
    1. 快照生成的RDB檔案是一種壓縮的二進位制檔案,可以方便的在網路中傳輸和儲存。通過RDB檔案可以方便的將redis資料恢復到某一歷史時刻,可以提高資料安全性,避免當機等意外對資料的影響。
    1. 適合大規模的資料恢復, RDB的啟動恢復效率高。
    1. 如果業務對資料完整性和一致性要求不高,RDB是很好的選擇。

劣勢

    1. 在redis檔案在時間點A生成,之後產生了新資料,還未到達另一次生成RDB檔案的條件,redis伺服器崩潰了,那麼在時間點A之後的資料會丟失掉,資料一致性不是完美的好, 如果可以接受這部分丟失的資料,可以用生成RDB的方式;
    1. 快照持久化方法通過呼叫fork()方法建立子執行緒。當redis記憶體的資料量比較大時,建立子執行緒和生成RDB檔案會佔用大量的系統資源和處理時間,對 redis處理正常的客戶端請求造成較大影響。
    1. 資料的完整性和一致性不高,因為RDB可能在最後一次備份時當機了。
    1. 備份時佔用記憶體,因為Redis 在備份時會獨立建立一個子程式,將資料寫入到一個臨時檔案(此時記憶體中的資料是原來的兩倍哦),最後再將臨時檔案替換之前的備份檔案。所以 Redis 的持久化和資料的恢復要選擇在夜深人靜的時候執行是比較合理的。

Q: 通過RDB檔案恢復資料?

答: 將dump.rdb 檔案拷貝到redis的安裝目錄的bin目錄下,重啟redis服務即可。在實際開發中,一般會考慮到物理機硬碟損壞情況,選擇備份dump.rdb 。


配置說明:

# Redis 配置檔案
cat > redis.conf <<'EOF'
# 密碼認證
requirepass WeiyiGeek.top

# 持久化檔案儲存路徑目錄
dir /data

# RDB核心規則之觸發儲存條件
說明:save <指定時間間隔> <執行指定次數更新操作>,滿足條件就將記憶體中的資料同步到硬碟中。官方出廠配置預設是 900秒內有1個更改,300秒內有10個更改以及60秒內有10000個更改, 則將記憶體中的資料快照寫入磁碟。
save 900 1    # 900秒(15分鐘)至少有1條key變化,其他同理
save 300 10
save 60 10000 #每60描述至少有1000個key發生變化時候則dump記憶體快照

# RDB 生成的檔名稱
dbfilename dump.rdb
# 是否壓縮(ZF壓縮方式),會佔用部分cpu資源,預設yes(建議開啟)
rdbcompression yes
# rdb 檔案校驗
rdbchecksum yes

# 備份程式出錯時,主程式停止接受寫入操作,預設yes
stop-writes-on-bgsave-error yes

# RDB自動觸發策略是否啟用,預設為yes
rdb-save-incremental-fsync yes
EOF

實際案例:

# 1.如上面配置所示,按配置情況觸發
# 比如在Redis服務終止的時候執行

# 2.手動儲存資料連線redis後使用命令save、bgsave觸發
127.0.0.1:6379> SAVE    #save 會阻塞redis伺服器直到完成持久化
OK
127.0.0.1:6379> BGSAVE  #bgsave 不會阻塞redis伺服器(它會fork一個子程式,由子程式進行持久化。)
OK
127.0.0.1:6379> quit


# 3.redis aof 持久化檔案完整性檢查與異常修正
/usr/local/redis/bin/redis-check-aof --fix  appendonly.aof
  # The AOF appears to start with an RDB preamble.
  # Checking the RDB preamble to start:
  # [offset 0] Checking RDB file --fix
  # [offset 27] AUX FIELD redis-ver = '5.0.10'
  # [offset 41] AUX FIELD redis-bits = '64'
  # [offset 53] AUX FIELD ctime = '1631088747'
  # [offset 68] AUX FIELD used-mem = '31554944'
  # [offset 84] AUX FIELD aof-preamble = '1'
  # [offset 86] Selecting DB ID 0
  # [offset 13350761] Checksum OK
  # [offset 13350761] \o/ RDB looks OK! \o/
  # [info] 157070 keys read
  # [info] 0 expires
  # [info] 0 already expired
  # RDB preamble is OK, proceeding with AOF tail...
  # 0x         27b66b0: Expected prefix '*', got: 'R'
  # AOF analyzed: size=116993914, ok_up_to=41641648, ok_up_to_line=1816854, diff=75352266
  # This will shrink the AOF from 116993914 bytes, with 75352266 bytes, to 41641648 bytes
  # Continue? [y/N]: y
  # Successfully truncated AOF


# 4.再利用rdb資料檔案進行恢復資料
root@dfbf8c0c0625:/data# ls
dump.rdb
# 將容器中的dump.rdb拷貝一份到宿主機中的`/var/lib/redis/`中,並需要在redis.conf中配置 `dir "/var/lib/redis/"`;
docker cp dfbf8c0c0625:/data/dump.rdb /var/lib/redis/dump.rdb

RDB檔案恢復資料流程

1、先備份一份 dump.rdb 為 dump_bak.rdb(模擬線上)
2、flushall 清空資料(模擬資料丟失,需要注意 flushall 也會觸發rbd持久化)
3、將 dump_bak.rdb 替換為 dump.rdb
4、重啟redis服務,恢復資料

2.AOF 方式

描述: AOF是redis對將所有的寫命令儲存到一個aof檔案中,根據這些寫命令實現資料的持久化和資料恢復。

WeiyiGeek.AOF備份還原


AOF檔案生成機制

答: 生成過程包括三個步驟,即命令追加、檔案寫入、檔案同步
redis 開啟AOF持久化功能之後,redis在執行完一個寫命令後,把執行的命令首先追加到redis內部的aof_buf緩衝區膜末尾,此時緩衝區的記錄還沒有寫到appendonly.aof檔案中。然後,緩衝區的寫命令會被寫入到 AOF 檔案,這一過程是檔案寫入過程。對於作業系統來說,呼叫write函式並不會立刻將資料寫入到硬碟,為了將資料真正寫入硬碟,還需要呼叫fsync函式,呼叫fsync函式即是檔案同步的過程,只有經過了檔案的同步過程,寫命令才真正的被儲存到了AOF檔案中。選項 appendfsync 就是配置同步的頻率的。

WeiyiGeek.AOF備份流程


AOF檔案重寫

  • 1、redis不斷的將寫命令儲存到AOF檔案中,導致AOF檔案越來越大,當AOF檔案體積過大時,資料恢復的時間也是非常長的,因此,redis提供了重寫或者說壓縮AOF檔案的功能。
    比如對key1初始值是0,呼叫incr命,100次,key1的值變為100,那麼其實直接一句set key1 100 就可以頂之前的100局呼叫,AOF重寫功能就是幹這個事情的。重寫時,可以呼叫BGREWRITEAOF命令重寫AOF檔案,與新建子執行緒bgsave命令的工作原理相似。也可以通過配置檔案配置什麼條件下對AOF檔案重寫。
  • 2、重寫的原理:Redis 會fork出一條新程式,讀取記憶體中的資料,並重新寫到一個臨時檔案中。並沒有讀取舊檔案(太大了)。最後替換舊的aof檔案
  • 3、重寫觸發機制:當AOF檔案大小是上次rewrite後大小的一倍且檔案大於64M時觸發,這裡的“一倍”和“64M” 可以通過配置檔案修改

優勢

    1. 該機制可以帶來更高的資料安全性,即資料持久化; 常規三種同步策略即每秒同步(非同步完成效率高)、每修改同步(同步插入修改刪除操作效率最低)和不同步;
    1. 由於該機制對日誌檔案的寫入操作採用的是append模式,即使過程中出現當機也不會破壞日誌檔案中已經存在的內容,如果資料不完整在Redis下次啟動之前, 通過redis-check-aof解決資料一致性問題;
    1. 如果日誌檔案體積過大可以啟動rewrite機制,即redis以append模式不斷的將修改資料寫到老的磁碟檔案中,同時建立新檔案記錄期間有哪些修改命令執行,此項極大的保證資料的安全性;
    1. AOF檔案可讀性強,其包含一個格式清晰、易於理解的日誌檔案用於記錄所有的修改操作(可通過此檔案完成資料的重構
    1. 資料的完整性和一致性更高
        

劣勢

    1. AOF檔案比RDB檔案較大, 對於相同數量的資料集而言;
    1. redis負載較高時,RDB檔案比AOF檔案具有更好的效能;
    1. RDB使用快照的方式持久化整個redis資料,而aof只是追加寫命令,因此從理論上來說,RDB比AOF方式更加健壯,另外,官方文件也指出,在某些情況下,AOF的確也存在一些bug,比如使用阻塞命令時,這些bug的場景RDB是不存在的。
    1. 因為AOF記錄的內容多,檔案會越來越大,資料恢復也會越來越慢。
    1. 根據同步策略的不同,AOF在執行效率上往往會慢於RDB,總的來說每秒同步策略的效率還是比較高的

Q: 如何觸發AOF快照?

答: 根據配置檔案觸發,可以是每次執行觸發,可以是每秒觸發,可以不同步。


Q: 如何根據AOF檔案恢復資料?

答: 正常情況下,將appendonly.aof 檔案拷貝到redis的安裝目錄的bin目錄下,重啟redis服務即可。但在實際開發中,可能因為某些原因導致 appendonly.aof 檔案格式異常,從而導致資料還原失敗,可以通過命令redis-check-aof --fix appendonly.aof進行修復 。


配置說明:

cat > redis.conf <<'EOF'
# 持久化資料儲存
dir "/data"

# 是否開啟AOF預設為否
appendonly yes

# AOF檔名字及路徑,若RDB路徑已設定這裡可不設定
appendfilename "appendonly.aof"

# AOF的3種模式,no(使用系統快取處理,快)、always(記錄全部操作,慢但比較安全)、everysec(每秒同步,折中方案,預設使用)
appendfsync everysec

# 重寫期間是否同步資料,預設為no
no-appendfsync-on-rewrite no

# 配置重寫觸發機制: 確保AOF日誌檔案不會過大,保持跟redis記憶體資料量一致。
# 配置說明:當AOF檔案大小是上次rewrite後大小的一倍且檔案大於64M時觸發(根據實際環境進行配置)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 256mb

# AOF重寫策略是否啟用,預設為yes
aof-rewrite-incremental-fsync yes

# 載入AOF時如果報錯則會繼續但寫入log,如果為no則不會繼續
aof-load-truncated yes

# Redis5.0有的功能AOF重寫及恢復可以使用RDB檔案及AOF檔案,速度更快,預設yes
aof-use-rdb-preamble yes
EOF

Q: 如何通過AOF檔案恢復資料流程?

1、執行flushall,模擬資料丟失
2、重啟 redis 服務,恢復資料
3、修改 appendonly.aof,模擬檔案異常
4、重啟 Redis 服務失敗,這同時也說明了RDB和AOF可以同時存在,且優先載入AOF檔案。
5、使用 redis-check-aof 校驗 appendonly.aof 檔案。
#  針對Redis aof 持久化檔案進行完整性檢測並進行修復
/usr/local/redis/bin/redis-check-aof --fix appendonly.aof
  # The AOF appears to start with an RDB preamble.
  # Checking the RDB preamble to start:
  # [offset 0] Checking RDB file appendonly.aof
  # [offset 27] AUX FIELD redis-ver = '5.0.10'
  # [offset 41] AUX FIELD redis-bits = '64'
  # [offset 53] AUX FIELD ctime = '1631088747'
  # [offset 68] AUX FIELD used-mem = '31554944'
  # [offset 84] AUX FIELD aof-preamble = '1'
  # [offset 86] Selecting DB ID 0
  # [offset 13350761] Checksum OK
  # [offset 13350761] \o/ RDB looks OK! \o/
  # [info] 157070 keys read
  # [info] 0 expires
  # [info] 0 already expired
  # RDB preamble is OK, proceeding with AOF tail...
  # 0x         27b66b0: Expected prefix '*', got: 'R'
  # AOF analyzed: size=116993914, ok_up_to=41641648, ok_up_to_line=1816854, diff=75352266
  # AOF is not valid. Use the option to try fixing it.

6、重啟Redis 服務後正常。 
# 利用源例項生成的aof檔案資料進行恢復到其它主機中。
redis-cli -h 17.20.0.2 -a password --pipe < applendonly.aof

注意:當你使用 flushall 清空資料的時候,重啟redis服務發現資料沒恢復,是因為 FLUSHALL 命令也被寫入AOF檔案中,會導致資料恢復失敗,所只需要刪除aof檔案中的flushall就行了

Tips : 在資料庫恢復時把 aof(append only file) 從中對redis資料庫操作的命令,增刪改操作的命令,執行了一遍即可。


實際案例:
描述: 用於非同步執行一個 AOF(AppendOnly File) 檔案重寫操作, 重寫會建立一個當前 AOF 檔案的體積優化版本,因為AOF為記錄每次的操作會導致實際記錄冗雜、使得檔案過大,所以需要做重寫操作。

重寫方式分為以下兩種:

# (1) AOF自動重寫:按配置檔案條件自動觸發重寫
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 256mb

# (2) AOF手動重寫:使用 redis-cli 連線到 server 端執行 bgrewriteaof 進行手動重寫。
# 注意:從 Redis 2.4 開始, AOF 重寫由 Redis 自行觸發, BGREWRITEAOF 僅僅用於手動觸發重寫操作。 
127.0.0.1:6379> BGREWRITEAOF 
# 即使 Bgrewriteaof 執行失敗,也不會有任何資料丟失,因為舊的 AOF 檔案在 Bgrewriteaof 成功之前不會被修改。
Background append only file rewriting started

如何抉擇 RDB OR AOF?

描述: 在實際生產環境中不要僅使用RDB(載入快、不保證資料完整性)或僅使用AOF(載入慢、資料完整性保證), 所以推薦綜合使用AOF和RDB兩種持久化機制進行資料備份。

  • AOF 機制保證資料不丟失,並且檔案有一定的可讀性即可以選擇性恢復一部分資料,所以作為資料恢復的第一選擇;
  • RDB 機制在AOF檔案都丟失或損壞不可用的時候,可以利用其冷備檔案來進行快速的資料恢復;

AOF和RDB同時工作特點:

  • 當 rdb 進行 snapshotting 時, redis 便不會再執行 AOF rewrite, 反之則一樣。
  • 當 rdb 進行 snapshotting 時, 其它使用者也在執行 BGREWRITEAOF 命令, 結果是隻有等RDB快照生成之後,才會執行AOF rewrite;
  • 當同時擁有rdb 快照檔案和AOF日誌檔案時, Redis 重啟時會優先使用AOF日誌進行恢復,。
  • 資料恢復完全依賴於磁碟持久化,如果rdb和aof上都沒有資料,資料就無法恢復了。

Tips : 重點再記錄、為保證資料容災建議啟用rdb與aof持久化機制,前者保證資料備份而後者保證資料的完整性。
Tips : 重點再記錄、當在伺服器中同時啟用rdb與aof持久化機制時,在redis服務啟動時優先載入AOF檔案(其資料的完整性)。


合併兩個不同例項的資料
描述: 我們可以利用如下方式進行叢集多個主節點持久化資料的合併。

(1) AOF 備份合併: 我們說過它實際上是一些列Redis的命令文字。
例如,假設有兩臺 Redis(6379, 6479),它們的AOF檔名分別為(6379.aof, 6479.aof),現在要將6379的資料合併到 6479.aof

# 首先
cp 6379.aof 6379.aof.bak, cp 6479.aof 6479.aof.bak

# 合併
cat 6379.aof 6479.aof > new.aof

# 檢查&修復
/usr/local/redis/bin/redis-check-aof --fix appendonly.aof

(2) RDB 備份合併: 注意以下方法可能由於服務端版本不同而有些許差異。
RDB格式如下:頭5個位元組是字元REDIS,之後4個位元組代表版本號,阿里的版本分別是 00 00 00 06,之後2個位元組 FE 00,FE是標識 00是資料庫,還好我們只有一個庫, 最後的結尾9個位元組 , FF 加上8個位元組的CRC64校驗碼(實在沒空弄,後來偷了一個懶)

# 1.線上服務使用的阿里雲的叢集版本redis服務,資料量1千萬,rdb檔案4GB,8個rdb檔案,每個500MB。
#檔案1 大小566346503,擷取尾部的9個位元組
dd bs=1 if=src_1.rdb of=1.rdb count=566346494
#檔案2 大小570214520,跳過頭部的11個位元組,再擷取尾部的9個位元組
dd bs=1 if=src_2.rdb of=2.rdb skip=11 count=570214500
...
#檔案8 大小569253061,跳過頭部的11個位元組,再擷取尾部的8個位元組,保留FF。
dd bs=1 if=src_8.rdb of=8.rdb skip=11 count=569253042

# 2.合併檔案(得到備份檔案dump.rdb)
cat 1.rdb > dump.rdb
cat 2.rdb >> dump.rdb
...
cat 8.rdb >> dump.rdb

# 3.檢查備份檔案(應該會提示沒有crc校驗)
redis-check-rdb dump.rdb 

# 4.修改配置檔案,因為資料庫備份檔案裡面不包含crc64的校驗碼,配置檔案中關閉選項。
rdbchecksum no

Tips : 資料恢復到此結束,此方法只適合用於臨時恢復和匯出資料,資料完整性不敢保證。

參考地址: https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format

其它工具:

  • https://github.com/leonchen83/redis-rdb-cli/ | 一個可以解析, 過濾, 分割, 合併 rdb 離線記憶體分析的工具. 也可以在兩個redis之前同步資料並允許使用者自定義同步服務來把redis資料同步到其他地方.

0x01 備份容災

一、備份

1.手動備份redis資料庫

#!/bin/bash
# 方式1.通過redis-cli內建命令將記憶體中的資料儲存到rdb檔案中
echo "auth 123456\nping\nsave\n" | redis-cli -h 127.0.0.1 -p 6379
echo "auth 123456\nping\nbgsave\n" | redis-cli -h 127.0.0.1 -p 6379

# 方式2.將遠端主機Redis-Server中儲存的資料儲存到本地/tmp/backup/目錄中。
redis-cli -h 127.0.0.1 -p 6379 --rdb /tmp/backup/app-6379.rdb

# 方式3.定時執行拷貝rdb與aof檔案進行備份。
# 例如: 每天0點執行一次 0 0 * * * sh /tmp/redisBackup.sh
current_date=$(date +%Y%m%d-%H%M%S)
BACKUPDIR="/backup/redis"
RDBFILE="/data/dump.rdb"
AOFFILE="/data/appendonly.aof"
# del_date=$(date -d -1day +%Y%m%d)
if [ ! -d ${BACKUPDIR} ];then
  mkdir -vp ${BACKUPDIR}
fi

if [ ! -f ${RDBFILE} ];then
  cp -a ${RDBFILE} ${BACKUPDIR}/${current_date}-dump.rdb
fi

if [ ! -f ${AOFFILE} ];then
  cp -a ${AOFFILE} ${BACKUPDIR}/${current_date}-appendonly.aof
fi

# 刪除七天前的備份
find ${BACKUPDIR} -type f -mtime +7 >> delete.log
find ${BACKUPDIR} -type f -mtime +7 -exec rm -rf {} \;

2.遷移Redis指定db-資料庫

方式1.同主機db遷移到另外一個dbn中

$ redis-cli -h localhost -a weiyigeek.top -n 0 keys "*" | while read key
do
  redis-cli -h localhost -a weiyigeek.top -n 0 --raw dump $key | perl -pe 'chomp if eof' | redis-cli -h localhost -a weiyigeek.top -n 12 -x restore $key 0
done 

方式2.跨主機遷移db

# redis 把db2 的資料遷移到 db14 裡 (注意:某些格式的資料不能完全已此種方式進行遷移)
# 需求分析:
'''1、建立兩個redis連線
   2、獲取所有的keys()
   3、獲取keys的型別:string hash'''

import redis
src_redis = redis.Redis(host='211.149.218.16',
                password='123456',
                port=6379,
                db=2)
target_redis = redis.Redis(host='211.149.218.16',
                password='123456',
                port=6379,
                db=14)
                
for key in src_redis.keys(): # redis獲取的資料都是bytes型別的,所以key的型別是 bytes 型別
    if src_redis.type(key) == b'string':# 也可以用decode() 把key轉換成string,這樣等號右邊就不需要加b
        v = src_redis.get(key)  #先獲取原來的資料
        target_redis.set(key,v) #set到新的資料庫裡
    else:
        all_hash_data = src_redis.hgetall(key) # 獲取hash型別裡面所有的資料,獲取出來的資料是字典格式的  但是有b,需要轉換
        for k,v in all_hash_data.items():# 因為獲取到字典格式的hash型別的原資料有b,所以需要用for迴圈來進行轉換後重新賦值給新的資料庫
            target_redis.hset(key,k,v) # key是外面的,k是裡面的key,v是k對應的value

3.Redis叢集資料備份與遷移

描述: 當我們需要備份或遷移Redis叢集時可以採用以下方案。

# (1) 備份叢集資料到本地目錄中(已rdb格式檔案儲存)。
redis-cli -a weiyigeek --cluster backup 172.16.243.97:6379 .
  # >>> Node 172.16.243.97:6379 -> Saving RDB...
  # SYNC sent to master, writing 178 bytes to './redis-node-172.16.243.97-6379-d97cb5b15b7130ca0bd5322758e0c2dce061fd7b.rdb'
  # Transfer finished with success.
  # >>> Node 172.16.183.95:6379 -> Saving RDB...
  # SYNC sent to master, writing 178 bytes to './redis-node-172.16.183.95-6379-94b8d3748dc47053454e657da8d6bb90e0081f2c.rdb'
  # Transfer finished with success.
  # >>> Node 172.16.24.214:6379 -> Saving RDB...
  # SYNC sent to master, writing 178 bytes to './redis-node-172.16.24.214-6379-2674f21a88a9573f51ec46f9dc248ad4a5c5974d.rdb'
  # Transfer finished with success.

# (2) 把 192.168.1.187:6379 上的資料匯入到 192.168.75.187:6379 這個節點所在的叢集,如有密碼將詢問
redis-cli -a weiyigeek --cluster import 192.168.75.187:6379 --cluster-from 192.168.1.187:6379 --cluster-from-askpass --cluster-copy

# (3) 遷移後利用 dbsize 命令檢視資料是否正確

Tips: 第三方redis叢集資料遷移工具專案參考(https://github.com/alibaba/RedisShake)


二、恢復

1.系統Redis使用者被刪除後配置資料恢復流程

描述:在系統刪除了配置檔案後以及使用者賬號後恢復方法流程,實際環境中建議利用rdb檔案進行重新部署。

  • Step1.Redis賬戶資料恢復首先確定系統中是否還有redis使用者。(如果拷貝過來的系統也安裝了redis,那麼肯定是會有redis賬戶)

  • Step2.如果發現有redis使用者以下步驟可以跳過,否則進行手動新增。

echo "redis:x:996:994:Redis Database Server:/var/lib/redis:/sbin/nologin" >> /etc/passwd
echo "redis:!!:17416::::::" >> /etc/shadow
echo "redis:x:994:" >> /etc/group
echo "redis:!::" >> /etc/gshadow
  • Step3.Redis配置檔案恢復, Redis的配置檔案恢復相對簡單一些,官方提供了CONFIG REWRITE命令重寫redis.conf配置檔案。
redis-cli
> CONFIG REWRITE
OK
  • Step4.修改配置檔案許可權
touch /etc/redis.conf
chown redis:redis /etc/redis.conf

2.Kubernetes中單例項異常資料遷移恢復實踐

方案1.利用其他kubernetes叢集進行恢復原k8s叢集的redis資料。

#!/bin/bash
# author: WeiyiGeek
# usage: ./K8SRedisRecovery.sh [aof|rdb] redis原持久化目錄
BACKUP_TYPE=$1
BACKUP_DIR=$2
DATA_DIR="$(pwd)/data"
echo "開始時間: $(date +%s)"

# 1.判斷備份檔案以及持久化檔案是否存在
if [ ! -d ${BACKUP_DIR} ];then echo -e "[Error] - Not Found ${BACKUP_DIR} Dirctory!"; return -1; fi
if [ ! -d ${DATA_DIR} ];then mkdir -vp ${DATA_DIR};else rm -rf "${DATA_DIR}/*"; fi

# 2.redis配置與k8s部署恢復redis清單
# tee redis.conf <<'EOF'
# bind 0.0.0.0
# port 6379
# daemonize no
# supervised no
# protected-mode no
# requirepass "weiyigeek"
# dir "/data"
# pidfile "/var/run/redis.pid"
# logfile "/var/log/redis.log"
# loglevel verbose
# maxclients 10000
# timeout 300
# tcp-keepalive 60
# maxmemory-policy volatile-lru
# slowlog-max-len 128
# lua-time-limit 5000
# save 300 10
# save 60 10000
# dbfilename "dump.rdb"
# rdbcompression yes
# rdb-save-incremental-fsync yes
# # appendonly yes
# appendfilename "appendonly.aof"
# appendfsync everysec
# # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# rename-command FLUSHDB WeiyiGeekFLUSHDB
# rename-command FLUSHALL WeiyiGeekFLUSHALL
# rename-command EVAL WeiyiGeekEVAL
# rename-command DEBUG WeiyiGeekDEBUG
# rename-command SHUTDOWN WeiyiGeekSHUTDOWN
# EOF

tee redisrecovery.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-recovery
  namespace: database
data:
  redis.conf: |+
    bind 0.0.0.0
    port 6379
    daemonize no
    supervised no
    protected-mode no
    requirepass "weiyigeek"
    dir "/data"
    pidfile "/var/run/redis.pid"
    logfile "/var/log/redis.log"
    loglevel verbose
    maxclients 10000
    timeout 300
    tcp-keepalive 60
    maxmemory-policy volatile-lru
    slowlog-max-len 128
    lua-time-limit 5000
    save 300 10
    save 60 10000
    dbfilename "dump.rdb"
    rdbcompression yes
    rdb-save-incremental-fsync yes
    # appendonly yes
    appendfilename "appendonly.aof"
    appendfsync everysec
    # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
    rename-command FLUSHDB WeiyiGeekFLUSHDB
    rename-command FLUSHALL WeiyiGeekFLUSHALL
    rename-command EVAL WeiyiGeekEVAL
    rename-command DEBUG WeiyiGeekDEBUG
    rename-command SHUTDOWN WeiyiGeekSHUTDOWN
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-recovery
  namespace: database
spec:
  serviceName: redisrecovery
  replicas: 1
  selector:
    matchLabels:
      app: redis-recovery
  template:
    metadata:
      labels:
        app: redis-recovery
    spec:
      containers:
      - name: redis
        image: redis:6.2.5-alpine
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 6379
          name: server
        command: [ "redis-server", "/conf/redis.conf"]
        volumeMounts:
        # 從configmap獲取的配置檔案,掛載到指定檔案中
        - name: conf
          mountPath: /conf/redis.conf
          subPath: redis.conf
        - name: data
          mountPath: /data
        # 時區設定
        - name: timezone
          mountPath: /etc/localtime
      volumes:
      - name: conf
        # 配置檔案採用configMap
        configMap:
          name: redis-recovery
          defaultMode: 0755
        # redisc持久化目錄採用hostPath卷
      - name: data
        hostPath:
          type: DirectoryOrCreate
          path: {PersistentDir}
        # 時區定義
      - name: timezone
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
---
apiVersion: v1
kind: Service
metadata:
  name: redisrecovery
  namespace: database
spec:
  type: ClusterIP
  ports:
  - port: 6379
    targetPort: 6379
    name: server
  selector:
    app: redis-recovery
EOF

# 3.刪除原有的恢復Pod
kubectl delete -f redisrecovery.yaml
# kubectl delete configmap -n database redis-recovery

# 4.判斷redis備份格式
if [ "${BACKUP_TYPE}" == "aof" ];then
  sed -i "s|# appendonly yes|appendonly yes|g" redisrecovery.yaml
  cp -a ${BACKUP_DIR}/appendonly.aof ${DATA_DIR}
else
  cp -a ${BACKUP_DIR}/dump.rdb ${DATA_DIR}
fi

# 5.更新redis備份恢復k8s資源清單中的持久化目錄
sed -i "s#{PersistentDir}#${DATA_DIR}#g" redisrecovery.yaml

# 6.建立configmap和部署redis備份恢復應用
# kubectl create configmap -n database redis-recovery --from-file=$(pwd)/redis.conf
kubectl create --save-config -f redisrecovery.yaml

# 7.驗證Pod狀態是否正常
flag=$(kubectl get pod -n database -o wide -l app=redis-recovery | grep -c "Running")
echo -e "\e[31m[Error]: Pod Status is not Running! \e[0m"
while [ ${flag} -ne 1 ];do
sleep 0.5
flag=$(kubectl get pod -n database -o wide -l app=redis-recovery | grep -c "Running")
done

# 8.驗證資料是否恢復
# apt install -y redis-tools
echo "[OK] redis-recovery Status is Running"
echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h redisrecovery.database.svc.cluster.local | grep -A 16 "# Keyspace"
while [ $? -ne 0 ];do
echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h redisrecovery.database.svc.cluster.local | grep -A 16 "# Keyspace"
done
echo "資料恢復完成......"
echo "完成時間: $(date +%s)"

命令執行示例:

# (1) 利用 AOF 檔案進行恢復百萬資料
/nfsdisk-31/datastore/redis/demo2# time ./K8sredisRecovery.sh aof /nfsdisk-31/data/database-data-redis-cm-0-pvc-00ee48e8-b1ca-4640-96c5-16f7265a2c61
  # # Keyspace
  # db0:keys=1000000,expires=0,avg_ttl=0
  # 資料恢復完成......
  # 完成時間: 1631067931

  # real    0m12.039s
  # user    0m0.647s
  # sys     0m0.199s

/nfsdisk-31/datastore/redis/demo2/data# ls -alh
  # -rw-r--r-- 1 root root  41M Sep  7 22:14 appendonly.aof
  # -rw-r--r-- 1 root root  41M Sep  8  2021 dump.rdb

# (2) 利用 rdb 檔案進行恢復 百萬資料
root@WeiyiGeek-107:/nfsdisk-31/datastore/redis/demo2# time ./K8sredisRecovery.sh rdb /nfsdisk-31/data/database-data-redis-cm-0-pvc-00ee48e8-b1ca-4640-96c5-16f7265a2c61
  # # Keyspace
  # db0:keys=993345,expires=0,avg_ttl=0
  # 資料恢復完成......
  # 完成時間: 1631069375

  # real    0m11.106s
  # user    0m0.606s
  # sys     0m0.192s

/nfsdisk-31/datastore/redis/demo2# ls -lah data/
  # -rw-r--r-- 1 root root  41M Sep  8  2021 dump.rdb
  # root@WeiyiGeek-107:/nfsdisk-31/datastore/redis/demo2#

WeiyiGeek.rdb與aof恢復對比

Tips : 從上述恢復結果可以看出以aof方式恢復的資料比rdb恢復的資料完整,但所載入的時間會隨著資料增大會使得AOF方式耗時比rdb耗時更多。


方案2.利用宿主機安裝編譯redis原始碼,進行恢復原k8s叢集的redis資料

#!/bin/bash
# author: WeiyiGeek
# usage: ./RedisRecovery.sh [aof|rdb] redis原持久化目錄
# 驗證環境: Ubuntu 20.04.1 LTS
# 指令碼說明: 將Redis資料恢復到物理中

if [ $# -eq 0 ];then
  echo -e "\e[31m[*] $0 [aof|rdb] redis原持久化目錄 \e[0m"
  exit
fi

BACKUP_TYPE=$1
BACKUP_DIR=$2
DATA_DIR="$(pwd)/data"
REDIS_DIR="/usr/local/redis"

echo "開始時間: $(date +%s)"

if [ ! -d ${BACKUP_DIR} ];then echo -e "[Error] - Not Found ${BACKUP_DIR} Dirctory!"; return -1; fi
if [ ! -d ${DATA_DIR} ];then mkdir -vp ${DATA_DIR};else rm -rf "${DATA_DIR}/*"; fi

## 1.基礎環境準備
# - 設定記憶體分配策略
sudo sysctl -w vm.overcommit_memory=1
# - 儘量使用實體記憶體(速度快)針對核心版本大於>=3.x
sudo sysctl -w vm.swapniess=1
# - SYN佇列長度設定,此引數可以容納更多等待連線的網路。
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# - 禁用 THP 特性減少記憶體消耗
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# redis 客戶端: apt remove redis-tools


## 2.REDIS 原始碼包
if [ ! -f /usr/local/redis/redis.conf ];then
REDIS_VERSION="redis-6.2.5"
REDIS_URL_TAR="https://download.redis.io/releases/${REDIS_VERSION}.tar.gz"
REDIS_TAR="${REDIS_VERSION}.tar.gz"

# redis 編譯環境以及編譯redis安裝在指定目錄
apt install -y gcc make gcc+ pkg-config
wget ${REDIS_URL_TAR} -O /tmp/${REDIS_TAR}
tar -zxf /tmp/${REDIS_TAR} -C /usr/local/
mv /usr/local/${REDIS_VERSION} ${REDIS_DIR}
cd ${REDIS_DIR} && make distclean
make PREFIX=${REDIS_DIR} install
cp -a ${REDIS_DIR}/redis.conf /etc/redis.conf
for i in $(ls -F ${REDIS_DIR}/bin | grep "*"| sed 's#*##g');do
  sudo chmod +700 ${REDIS_DIR}/bin/${i}
  sudo ln -s ${REDIS_DIR}/bin/${i} /usr/local/bin/${i}
done
fi

# 物理機執行可以將daemonize設定為後臺執行。
tee ${REDIS_DIR}/redis.conf <<'EOF'
bind 0.0.0.0
port 6379
daemonize yes
supervised no
protected-mode no
requirepass "weiyigeek"
dir "/data"
pidfile "/var/run/redis.pid"
logfile "/var/log/redis.log"
# loglevel verbose
maxclients 10000
timeout 300
tcp-keepalive 60
maxmemory-policy volatile-lru
slowlog-max-len 128
lua-time-limit 5000
save 900 1
save 300 100
save 60 10000
dbfilename "dump.rdb"
rdbcompression yes
rdb-save-incremental-fsync yes
# appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
rename-command FLUSHDB WeiyiGeekFLUSHDB
rename-command FLUSHALL WeiyiGeekFLUSHALL
rename-command EVAL WeiyiGeekEVAL
rename-command DEBUG WeiyiGeekDEBUG
rename-command SHUTDOWN WeiyiGeekSHUTDOWN
EOF

# 3.判斷要使用的備份格式
if [ "${BACKUP_TYPE}" == "aof" ];then
  sed -i "s|# appendonly yes|appendonly yes|g" ${REDIS_DIR}/redis.conf
  cp -a ${BACKUP_DIR}/appendonly.aof ${DATA_DIR}
else
  cp -a ${BACKUP_DIR}/dump.rdb ${DATA_DIR}
fi

sed -i "s#/data#${DATA_DIR}#g" ${REDIS_DIR}/redis.conf

# 4.啟動redis服務恢復資料
${REDIS_DIR}/bin/redis-server ${REDIS_DIR}/redis.conf 
ps -aux | grep redis-server
echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h 127.0.0.1 | grep -A 16 "# Keyspace"
while [ $? -ne 0 ];do
echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h 127.0.0.1 | grep -A 16 "# Keyspace"
done
echo "資料恢復完成......"
echo "完成時間: $(date +%s)"

方案3.利用Kubernetes部署的Redis叢集,進行恢復原k8s叢集的redis資料

#!/bin/bash
# author: WeiyiGeek
# K8s 中 redis 叢集資料恢復
# useage: K8SRedisClusterRecovery.sh [single|cluster] PODMATCH K8SVOLUMNDIR 

if [ $# -eq 0 ];then
  echo -e "\e[31m[*] $0 [single|cluster] PODMATCH K8SVOLUMNDIR \e[0m"
  echo -e "\e[31m[*] PODMATCH : redis-cluster {異常叢集 statefulset 資源物件名稱} \e[0m"
  echo -e "\e[31m[*] K8SVOLUMNDIR : /nfsdisk-31/data  {K8S 持久化跟目錄} \e[0m"
  exit
fi


RECTARGET="${1}"
PODMATCH="${2}"
K8SVOLUMNDIR="${3}"
AUTH="weiyigeek"
PWD=$(pwd)
AOFNAME="appendonly.aof"
DATADIR="${PWD}/database"
K8SSVCNAME="database.svc.cluster.local"


# 1.原`Redis`叢集nodes資訊一覽,獲取aof檔案路徑。
grep "myself,master" ${K8SVOLUMNDIR}/*${PODMATCH}-[0-9]-*/nodes.conf | head -n 3 > /tmp/nodes.log
cat /tmp/nodes.log
node1=$(grep "0-5460" /tmp/nodes.log | cut -d ":" -f 1)
aofpath1=${node1%/*}
node2=$(grep "5461-10922" /tmp/nodes.log | cut -d ":" -f 1)
aofpath2=${node2%/*}
node3=$(grep "10923-16383" /tmp/nodes.log | cut -d ":" -f 1)
aofpath3=${node3%/*}


# 2.驗證aof檔案是否存在並拷貝到當前路徑下,合併AOF檔案到當前目錄的data下。
if [ ! -f ${aofpath1}/${AOFNAME} -o ! -f ${aofpath2}/${AOFNAME} -o ! -f ${aofpath3}/${AOFNAME} ];then echo -e "\e[31m[-] ${AOFNAME} file not found \e[0m";exit;fi
cp ${aofpath1}/${AOFNAME} ./1.${AOFNAME}
cp ${aofpath2}/${AOFNAME} ./2.${AOFNAME}
cp ${aofpath3}/${AOFNAME} ./3.${AOFNAME}
if [ ! -d ${DATADIR} ];then mkdir -v ${DATADIR};fi
cat *.aof > ${DATADIR}/${AOFNAME}
# 校驗合併的原叢集aof檔案並嘗試進行修復
echo "y" | /usr/local/redis/bin/redis-check-aof --fix ${DATADIR}/${AOFNAME}
ls -alh ${DATADIR}

# 3.驗證原叢集相關檔案是否有誤(如有誤需要人工進行相應處理)
echo -e "\e[32m[*] 請驗證原Redis叢集Nodes資訊是否無誤? 請輸入[Y|N] \e[0m"
read flag
if [ "${flag}" == "N" -o "${flag}" == "n" ];then echo -e "\e[31m[-] FAILED: 需要人工進行干預處理. \e[0m";exit;fi


# 4.判斷恢復到單例項還是cluster叢集中。
if [ "${RECTARGET}" == "single" ];then
  echo -e "\e[32m[*] 正在進行異常的K8S叢集 -> 單例項資料恢復! \e[0m"
  ./K8SRedisRecovery.sh aof ${DATADIR}
  return 0
else
  echo -e "\e[32m[*] 正在進行異常的K8S叢集 -> 叢集資料恢復! \e[0m"
fi

# 5.redis叢集資源清單(注意:此處預設是採用nfs型別的動態卷)
tee Redis-cluster-6.2.5.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cluster-recovery
  namespace: database
data:
  update-node.sh: |
    #!/bin/sh
    if [ ! -f /data/nodes.conf ];then touch /data/nodes.conf;fi
    REDIS_NODES="/data/nodes.conf"
    sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${REDIS_NODES}
    exec "$@"
  redis.conf: |+
    port 6379
    protected-mode no
    masterauth {RedisAuthPass}
    requirepass {RedisAuthPass}
    dir /data
    dbfilename dump.rdb
    rdbcompression yes
    no-appendfsync-on-rewrite no
    appendonly yes 
    appendfilename appendonly.aof 
    appendfsync everysec
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 128mb
    # 叢集模式開啟
    cluster-enabled yes 
    cluster-config-file /data/nodes.conf
    cluster-node-timeout 5000
    slave-read-only yes
    # 當負責一個插槽的主庫下線且沒有相應的從庫進行故障恢復時叢集仍然可用
    cluster-require-full-coverage no
    # 只有當一個主節點至少擁有其他給定數量個處於正常工作中的從節點的時候,才會分配從節點給叢集中孤立的主節點
    cluster-migration-barrier 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster-recovery
  namespace: database
spec:
  serviceName: redisclusterrecovery
  replicas: 6
  selector:
    matchLabels:
      app: redis-cluster-recovery
  template:
    metadata:
      labels:
        app: redis-cluster-recovery
    spec:
      containers:
      - name: redis
        image: redis:6.2.5-alpine
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 6379
          name: client
        - containerPort: 16379
          name: gossip
        command: ["/conf/update-node.sh", "redis-server", "/conf/redis.conf"]
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        volumeMounts:
        - name: conf
          mountPath: /conf
          readOnly: false
        - name: data
          mountPath: /data
          readOnly: false
        - name: timezone
          mountPath: /etc/localtime
      volumes:
      - name: conf
        configMap:
          name: redis-cluster-recovery
          defaultMode: 0755
      - name: timezone 
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"
      resources:
        requests:
          storage: 1Gi
---
# headless Service
apiVersion: v1
kind: Service
metadata:
  name: redisclusterrecovery
  namespace: database
spec:
  clusterIP: "None"
  ports:
  - port: 6379
    targetPort: 6379
    name: client
  - port: 16379
    targetPort: 16379
    name: gossip
  selector:
    app: redis-cluster-recovery
EOF
sed -i "s#{RedisAuthPass}#${AUTH}#g" Redis-cluster-6.2.5.yaml
sed "s#replicas: 6#replicas: 0#g" Redis-cluster-6.2.5.yaml > Redis-cluster-6.2.5-empty.yaml


# 6.部署恢復資料的redis叢集:判斷是否存在舊的資源物件,是則清理相關檔案,否則建立叢集。
sts=$(kubectl get sts -n database  | grep -c "redis-cluster-recovery")
if [ ${sts} -eq 1 ];then 
  # 將副本數量至為0
  kubectl apply -f Redis-cluster-6.2.5-empty.yaml; 
  podrun=$(kubectl get pod -n database  | grep -c "redis-cluster-recovery")
  while [ ${run} -ne 0 ];do podrun=$(kubectl get pod -n database  | grep -c "redis-cluster-recovery");sleep 5;echo -n .; done
  find ${K8SVOLUMNDIR}/database-data-redis-cluster-recovery-* -type f -delete;
  # 清理後將副本數量至為6
  kubectl apply -f Redis-cluster-6.2.5.yaml
  run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running")
  while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running");sleep 2;echo -n .; done
else
  # 新建部署叢集資源清單 
  kubectl create --save-config -f Redis-cluster-6.2.5.yaml
  run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running")
  while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running");sleep 2;echo -n .; done
fi

echo -e "\e[32m[*] Happy,redis-cluster-recovery Pod is Running....\e[0m"
kubectl get pod -n database -l app=redis-cluster-recovery


# 7.redis叢集快速建立配置
echo -e "yes" | redis-cli -h redisclusterrecovery.${database.svc.cluster.local} -a weiyigeek --cluster create --cluster-replicas 1 $(kubectl get pods -n database -l app=redis-cluster-recovery -o jsonpath='{range.items[*]}{.status.podIP}:6379 '| sed "s# :6379 ##g")
# 將其他兩個Master卡槽歸於一個Master
redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local} --no-auth-warning -a weiyigeek cluster nodes | grep "master" > /tmp/rnodes.log
cat /tmp/rnodes.log
m1=$(grep "0-5460" /tmp/rnodes.log | cut -d " " -f 1)
m2=$(grep "5461-10922" /tmp/rnodes.log | cut -d " " -f 1)
m3=$(grep "10923-16383" /tmp/rnodes.log | cut -d " " -f 1)
redis-cli --no-auth-warning -a weiyigeek --cluster reshard --cluster-from ${m2} --cluster-to ${m1} --cluster-slots 5462 --cluster-yes redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local}:6379 > /dev/null
redis-cli --no-auth-warning -a weiyigeek --cluster reshard --cluster-from ${m3} --cluster-to ${m1} --cluster-slots 5461 --cluster-yes redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local}:6379 > /dev/null
redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local --no-auth-warning -a weiyigeek cluster nodes
echo -e "\e[32m[*] 請驗證Nodes資訊是否無誤? 請輸入[Y|N] \e[0m"
read flag
if [ "${flag}" == "N" -o "${flag}" == "n" ];then echo -e "\e[31m[-] FAILED: 需要人工進行干預處理. \e[0m";exit;fi


# 8.將所有slots都歸於一個master節點後我們需要,將redisclusterrecovery 資源物件所管理的Pod先關閉。
# 將副本數量至為0
kubectl apply -f Redis-cluster-6.2.5-empty.yaml; 
podrun=$(kubectl get pod -n database  | grep -c "redis-cluster-recovery")
while [ ${run} -ne 0 ];do podrun=$(kubectl get pod -n database  | grep -c "redis-cluster-recovery");sleep 5;echo -n .; done
# find ${K8SVOLUMNDIR}/database-data-redis-cluster-recovery-* -type f -delete;

# 此處只清空了redis持久化資料檔案。
master=$(find /nfsdisk-31/data/*redis-cluster-recovery-0* -type d)
rm -rf ${master}/*.aof ${master}*/.rdb
cp ${DATADIR}/appendonly.aof {${master}}

# 清理後將副本數量至為6
kubectl apply -f Redis-cluster-6.2.5.yaml
run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running")
while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running");sleep 2;echo -n .; done

echo -e "\e[32m[*] Happy,redis-cluster-recovery Pod is Running....\e[0m"
kubectl get pod -n database -l app=redis-cluster-recovery


# 9.處理K8s重啟redis叢集出現的fail問題,我們可以將錯誤節點剔出叢集並重新指定節點資訊加入到叢集之中
redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local --no-auth-warning -a weiyigeek cluster nodes | grep "fail" > /tmp/errnodes.log
# 將該從節點剔出叢集
for err in $(cat /tmp/errnodes.log | cut -d " " -f 1);do
  redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -a weiyigeek cluster forget ${errid}
done
# 重新將該節點加入叢集
for ip in $(kubectl get pods -n database -l app=redis-cluster-recovery -o jsonpath='{range.items[*]}{.status.podIP} '| sed "s# :6379 ##g");do
  redis-cli -h  -a weiyigeek cluster meet ${ip} 6379
done

# 10.叢集狀態檢測以及重新分配slots卡槽到Master節點
redis-cli -h redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster check redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local:6379
redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster rebalance --cluster-threshold 1 --cluster-use-empty-masters redis-cluster-recovery-1.redisclusterrecovery.database.svc.cluster.local:6379

# 11.驗證分配的slots卡槽到各個Master節點節點資訊。
redis-cli -h redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster check redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local:6379


# 12.主master節點驗證keyspace資料
redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -c -a weiyigeek info keyspace

# 13.任何節點訪問恢復的資料
redis-cli -h redisclusterrecovery.database.svc.cluster.local -c -a weiyigeek 

3.當Redis叢集中出現從節點slave,fail,noaddr問題進行處理恢復流程。

  • Step 1.利用cluster nodes檢視你叢集狀態,發現其中一個從節點異常(是Fail狀態)。
f86464011d9f8ec605857255c0b67cff1e794c19 :0@0 slave,fail,noaddr 2cb35944b4492748a8c739fab63a0e90a56e414a 

  • Step 2.在問題節點上檢視節點狀態,發現它已脫離叢集,且其ID都已發生了變化.
127.0.0.1:6379> cluster nodes
0cbf44ef3f9c3a8a473bcd303644388782e5ee78 192.168.109.132:6379@16379 myself,master - 0 0 0 connected 0-5461

Tips : 若id沒發生變化,直接重啟下該從節點就能解決。


  • Step 3.但如果ID與Node IP都發生變化時,此時我們需要將該從節點剔出叢集。
# 在叢集每個正常節點上執行cluster forget 故障從節點id
echo 'cluster forget f86464011d9f8ec605857255c0b67cff1e794c19' | /usr/local/bin/redis-cli -a "weiyigeek"
  • Step 4.我們重新將該節點加入叢集,此時我們只需要 在叢集內任意節點上執行cluster meet命令加入新節點,握手狀態會通過資訊在叢集內傳播,這樣其他節點會自動發現新節點併發起握手流程。
# 1.使用叢集強制聯絡指定節點(握手)
echo 'cluster meet 192.168.109.132 6379' | /usr/local/bin/redis-cli -p 6379 -a "密碼"

# 2.從節上執行cluster replicate 主節點id( 配置主從關係 )
echo 'cluster replicate 2cb35944b4492748a8c739fab63a0e90a56e414a' | /usr/local/bin/redis-cli -p 6383 -a "密碼"
  • Step 5.最後檢測叢集是否恢復正常,執行如下命令即可。
echo 'cluster nodes' | /usr/local/bin/redis-cli -p 6384 -a "密碼"
38287a7e715c358b5537a369646e9698a7583459 192.168.109.132:6383@16383 slave 2cb35944b4492748a8c739fab63a0e90a56e414a 0 1615233239757 8 connected
2cb35944b4492748a8c739fab63a0e90a56e414a 192.168.109.133:6383@16383 master - 0 1615233239000 8 connected 0-5461
.......

原文地址: https://blog.weiyigeek.top/2019/4-17-51.html
文章書寫不易,如果您覺得這篇文章還不錯的,請給這篇專欄 【點個贊、投個幣、收個藏、關個注,轉個發】(人間五大情),這將對我的肯定,謝謝!。

本文章來源 我的Blog站點WeiyiGeek 公眾賬號 以及 我的BiliBili專欄 (技術交流、友鏈交換請郵我喲),謝謝支援!(๑′ᴗ‵๑) ❤
歡迎各位志同道合的朋友一起學習交流,如文章有誤請留下您寶貴的知識建議,通過郵箱【master#weiyigeek.top】聯絡我喲!

相關文章