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

来源:https://www.cnblogs.com/WeiyiGeek/archive/2022/04/14/16143370.html
-Advertisement-
Play Games

本章目錄 0x00 數據持久化 1.RDB 方式 2.AOF 方式 如何抉擇 RDB OR AOF? 0x01 備份容災 一、備份 1.手動備份redis資料庫 2.遷移Redis指定db-資料庫 3.Redis集群數據備份與遷移 二、恢復 1.系統Redis用戶被刪除後配置數據恢復流程 2.Kub ...


本章目錄

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】聯繫我喲!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • bat文件是dos下的批處理文件。批處理文件是無格式的文本文件,它包含一條或多條命令。它的文件擴展名為 .bat 或 .cmd。 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 一、Zabbix 簡介 Zabbix 可以用來監控各種網路參數,來保證伺服器和系統的安全運行。並且 Zabbix 還提供了靈活的通知機制,以此來讓系統管理員快速定位/解決存在的各種問題。是一個基於 Web 界面提供的分散式系統監控以及網路監控功能 ...
  • 1. 什麼是資料庫 資料庫是“按照數據結構來組織、存儲和管理數據的倉庫”。是一個長期存儲在電腦內的、有組織的、可共用的、統一管理的大量數據的集合。簡單的來說就像是一個大型的衣櫃,你所有春夏秋冬的衣服都被統一放在了一個衣櫃里,然後分類,分季節的有序擺放好。資料庫的優點重點在於體量足夠的大,一般的數據 ...
  • 12月21日,在第十一屆中國資料庫技術大會(DTCC)2020數據風雲獎評選活動中,華為雲資料庫GaussDB(openGauss)和金融行業核心資料庫上雲解決方案分別榮獲“年度最佳創新產品獎”、“年度最佳創新解決方案獎”。 DTCC2020是由IT168 旗下 ITPUB 企業社區平臺主辦的頂級數 ...
  • 近年來,雲計算已成為主流,企業從自身利益出發,或是不願意被單一雲服務商鎖定,或是業務和數據冗餘,或是出於成本優化考慮,會嘗試將部分或者全部業務從線下機房遷移到雲或者從一個雲平臺遷移到另一個雲平臺,業務遷移涉及到數據的遷移。正好 JuiceFS 已經對接了各種對象存儲的 API ,也實現了數據同步的邏 ...
  • 本文介紹瞭如何使用 SQL 的 SELECT 語句來檢索單個表列、多個表列以及所有表列。也介紹瞭如何返回不同的值以及如何註釋代碼。 一、SELECT 語句 正如 學習 SQL 之前需要瞭解的基礎知識 所述,SQL 語句是由簡單的英語單詞構成的。這些單詞稱為關鍵字,每個 SQL 語句都是由一個或多個關 ...
  • 說起雲會議大家第一想起的就是疫情期間,上網課也好,網上辦公也好等等。在疫情到來之前線上會議就已經有了雛形但是並不完善,但是疫情開始之後,線上會議蓬勃發展,各種雲會議應用而生。雲會議得到了空前的發展,那麼當我們需要一場高質量的雲會議時應該怎麼選擇呢? 雲會議帶給我們體驗最重要的三點:夠清晰、夠流暢、夠 ...
  • 本文為 SQL 初學者介紹了 SQL 究竟是什麼,以及它能做什麼事情。因為 SQL 是用來與資料庫打交道的,所以,我們也介紹了一些基本的資料庫術語。 一、資料庫基礎 你正在讀這這一篇文章,這表明你需要以某種方式與資料庫打交道。SQL 正是用來實現這一任務的語言,因此在學習 SQL 之前,你應該對數據 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...