【Redis技術(shù)探索】「數(shù)據(jù)遷移實戰(zhàn)」手把手教你如何實現(xiàn)在線+離線模式進行遷移Redis數(shù)據(jù)實戰(zhàn)指南(在線同步數(shù)據(jù))

從實戰(zhàn)出發(fā)使用RedisShake進行Redis數(shù)據(jù)在線+離線模式遷移指南

RedisShake基本介紹

RedisShake是基于redis-port基礎(chǔ)上進行改進的是一款開源的Redis遷移工具,支持Cluster集群的在線遷移與離線遷移(備份文件導入)。數(shù)據(jù)可平滑遷移,當部署在其他云廠商Redis服務(wù)上的Cluster集群數(shù)據(jù),由于SYNC、PSYNC命令被云廠商禁用,無法在線遷移時,可以選擇離線遷移。

RedisShake使用背景

RedisShake是一個用于在兩個Redis實例之間同步數(shù)據(jù)的工具,滿足非常靈活的同步與遷移需求。Redis實例之間的關(guān)系其中可能存在(standalone->standalone),(standalone->Cluster),(Cluster->Cluster)等。目前,比較常用的一個數(shù)據(jù)遷移工具是Redis-Shake ,這是阿里云Redis和MongoDB團隊開發(fā)的一個用于 Redis 數(shù)據(jù)同步的工具。

RedisShake功能說明

RedisShake主要是支持Redis的RDB文件的解析、恢復、備份、同步四個功能:

  • 恢復(restore):將 RDB 文件恢復到目標Redis數(shù)據(jù)庫。
  • 備份(dump):將源 Redis 的全量數(shù)據(jù)通過RDB文件備份起來。
  • 解析(decode):讀取 RDB 文件,并以 JSON 格式解析存儲。
  • 同步(sync):支持源redis和目的redis的數(shù)據(jù)同步,支持全量和增量數(shù)據(jù)的遷移,支持從云下到阿里云云上的同步,也支持云下到云下不同環(huán)境的同步,支持單節(jié)點、主從版、集群版之間的互相同步。
  • 同步(rump):支持源 Redis 和目的 Redis 的數(shù)據(jù)同步,僅支持全量遷移。采用scan和restore命令進行遷移,支持不同云廠商不同redis版本的遷移。

注意:如果源端是集群版,可以啟動一個RedisShake,從不同的db結(jié)點進行拉取,同時源端不能開啟move slot功能;對于目的端,如果是集群版,寫入可以是1個或者多個db結(jié)點。

Redis-Shake特性概覽

  • 高性能:全量同步階段并發(fā)執(zhí)行,增量同步階段異步執(zhí)行,能夠達到毫秒級別延遲(取決于網(wǎng)絡(luò)延遲)。同時,我們還對大key同步進行分批拉取,優(yōu)化同步性能。
  • 在 Redis 5.0、Redis 6.0 和 Redis 7.0 上測試
  • 支持使用lua自定義過濾規(guī)則
  • 支持大實例遷移
  • 支持restore模式和sync模式
  • 支持阿里云 Redis 和 ElastiCache
  • 監(jiān)控體系:用戶可以通過我們提供的restful拉取metric來對redis-shake進行實時監(jiān)控:curl 127.0.0.1:9320/metric。
  • 數(shù)據(jù)校驗:如何校驗同步的正確性?可以采用我們開源的redis-full-check。
  • 支持版本:支持2.8-5.0版本的同步,此外還支持codis,支持云下到云上,云上到云上,云上到云下(阿里云目前支持主從版),其他云到阿里云等鏈路,幫助用戶靈活構(gòu)建混合云場景。
  • 斷點續(xù)傳。支持斷開后按offset恢復,降低因主備切換、網(wǎng)絡(luò)抖動造成鏈路斷開重新同步拉取全量的性能影響。
image

RedisShake執(zhí)行過程

  1. 啟動Redis-shake進程,這個進程模擬了一個 Redis 實例,Redis-shake的基本原理就是模擬一個Slave從節(jié)點加入源Redis集群,然后進行增量的拉?。ㄍㄟ^psync命令)。

  2. Redis-shake進程和數(shù)據(jù)遷出的源實例進行數(shù)據(jù)的全量拉取同步,并回放,這個過程和 Redis 主從實例的全量同步是類似的。如下圖所示。

image

詳細分析上述同步原理

  1. 源Redis服務(wù)實例相當于主庫,Redis-shake相當于從庫,它會發(fā)送psync指令給源Redis服務(wù)實例。

  2. 源Redis實例先把RDB文件傳輸給 Redis-shake ,Redis-shake 會把RDB文件發(fā)送給目的實例。

  3. 源實例會再把增量命令發(fā)送給 Redis-shake ,Redis-shake負責把這些增量命令再同步給目的實例。

如果源端是集群模式,只需要啟動一個redis-shake進行拉取,同時不能開啟源端的move slot操作。如果目的端是集群模式,可以寫入到一個結(jié)點,然后再進行slot的遷移,當然也可以多對多寫入。

目前,redis-shake到目的端采用單鏈路實現(xiàn),對于正常情況下,這不會成為瓶頸,但對于極端情況,qps比較大的時候,此部分性能可能成為瓶頸,后續(xù)我們可能會計劃對此進行優(yōu)化。另外,redis-shake到目的端的數(shù)據(jù)同步采用異步的方式,讀寫分離在2個線程操作,降低因為網(wǎng)絡(luò)時延帶來的同步性能下降。

Redis-Shake安裝使用

主要有兩種方式:下載Release版本的可執(zhí)行二進制包、下載源碼文件進行編譯操作這兩種方式。

下載Release版本的可執(zhí)行二進制包

Download from Release

image

點擊下載就可以進行直接使用Redis-Shake服務(wù)。

下載源碼文件進行編譯操作

除了直接下載可執(zhí)行包之外,還可以下載源碼之后,可以進行運行build.sh文件執(zhí)行進行編譯源碼,生成可執(zhí)行包。可以根據(jù)上面的下載中source code進行下載。

image

或者可以針對于Git進行clone源碼倉庫,如下所示。

git clone https://github.com/alibaba/RedisShake
cd RedisShake
sh build.sh

運行Redis-Shake服務(wù)

首先如果需要進行同步和重放,則需要進行編輯sync.toml文件以及編輯restore.toml.

  • redis-shake 支持三種數(shù)據(jù)遷移模式:sync、restore 和 scan:
  • 快速開始:數(shù)據(jù)遷移(使用 sync 模式)
  • 快速開始:從dump.rdb恢復數(shù)據(jù)(使用 restore 模式)
  • 快速開始:數(shù)據(jù)遷移(使用 scan 模式)
  • 使用 filters 做數(shù)據(jù)清洗
  • 運行日志
  • 運行監(jiān)控
image

啟動同步sync運行機制

我們打開或者編輯sync.toml

image
sync.toml文件內(nèi)容

當我們編輯sync.toml文件之后,可以進行配置我們實際情況下的source源redis實例以及target目標redis實例。之后可以配置對應(yīng)的cpu和相關(guān)性能的配置。下面針對于配置進行相關(guān)的配置介紹

type = "sync" # 同步機制實現(xiàn)

[source] # 源Redis服務(wù)實例
version = 5.0 # 填寫Redis源服務(wù)版本, 例如:2.8, 4.0, 5.0, 6.0, 6.2, 7.0, ...。
address = "127.0.0.1:6379" # 源Redis服務(wù)實例 地址+端口
username = "" # 如果Redis沒有配置ACL,則可以不填寫,否則需要填寫用戶名 
password = "" # 如果Redis沒有配置ACL,則可以不填寫,否則需要填寫密碼
tls = false # 是否開啟tls安全機制
elasticache_psync = "" # 是否支持AWS的elasticache

[target]
type = "standalone" # 選擇Redis的類型:"standalone:單機模式" or "cluster:集群模式"
version = 5.0  # 填寫Redis源服務(wù)版本, 例如:2.8, 4.0, 5.0, 6.0, 6.2, 7.0, ...。
# 如果目標Redis服務(wù)實例屬于cluster集群模式, 那么可以寫入其中一個節(jié)點的地址和端口.
# redis-shake 會通過`cluster nodes` 命令獲取其他的節(jié)點地址和端口
address = "127.0.0.1:6380" # 填寫的對應(yīng)的ip加端口
username = "" # 如果Redis沒有配置ACL,則可以不填寫,否則需要填寫用戶名 
password = "" # 如果Redis沒有配置ACL,則可以不填寫,否則需要填寫密碼
tls = false # 是否開啟tls安全機制

[advanced]
dir = "data" # 數(shù)據(jù)同步的存儲目錄

# 設(shè)置使用的最大CPU核心數(shù), 如果設(shè)置了0 代表著 使用 runtime.NumCPU() 實際的cpu cores數(shù)量
ncpu = 4

# 開啟pprof性能檢測的port, 0代表著禁用
pprof_port = 0 

# 開啟metric port端口, 0代表著禁用
metrics_port = 0

# log的相關(guān)設(shè)置
log_file = "redis-shake.log" # 設(shè)置對應(yīng)的日志文件名稱
log_level = "info" # debug, info or warn # 設(shè)置對應(yīng)的日志級別
log_interval = 5 # in seconds # 日志打印頻次

# redis-shake gets key and value from rdb file, and uses RESTORE command to
# create the key in target redis. Redis RESTORE will return a "Target key name
# is busy" error when key already exists. You can use this configuration item
# to change the default behavior of restore:
# panic:   redis-shake will stop when meet "Target key name is busy" error.
# rewrite: redis-shake will replace the key with new value.
# ignore:  redis-shake will skip restore the key when meet "Target key name is busy" error.
rdb_restore_command_behavior = "rewrite"  # restore的操作類型:panic, rewrite or skip

# pipeline的大小數(shù)量閾值
pipeline_count_limit = 1024

# Client query buffers accumulate new commands. They are limited to a fixed
# amount by default. This amount is normally 1gb.
target_redis_client_max_querybuf_len = 1024_000_000

# In the Redis protocol, bulk requests, that are, elements representing single
# strings, are normally limited to 512 mb.
target_redis_proto_max_bulk_len = 512_000_000

Redis單機實例同步到Redis單機實例

修改sync.toml,改為如下配置。

[source]
address = "ip:6379"
password = ""

[target]
type = "standalone"
address = "ip:6379"
password = "r-bbbbb:xxxxx"

啟動 redis-shake:

./redis-shake sync.toml

Redis單機實例同步到Redis集群實例

修改 sync.toml,改為如下配置:

[source]
address = "r-aaaaa.redis.zhangbei.rds.aliyuncs.com:6379"
password = "r-aaaaa:xxxxx"

[target]
type = "cluster"
address = "192.168.0.1:6379" # 這里寫集群中的任意一個節(jié)點的地址即可
password = "r-ccccc:xxxxx"
啟動 redis-shake:
./redis-shake sync.toml

Redis集群實例同步到Redis集群實例

方法1:手動起多個 redis-shake

集群C有四個節(jié)點:

  • 192.168.0.1:6379
  • 192.168.0.2:6379
  • 192.168.0.3:6379
  • 192.168.0.4:6379

把4個節(jié)點當成4個單機實例,參照單機到集群 部署 4 個 redis-shake 進行數(shù)據(jù)同步。不要在同一個目錄啟動多個 redis-shake,因為 redis-shake 會在本地存儲臨時文件,多個 redis-shake 之間的臨時文件會干擾,正確做法是建立多個目錄。

方法2:借助 cluster_helper.py 啟動

腳本cluster_helper.py可以方便啟動多個redis-shake從集群遷移數(shù)據(jù),效果等同于方法1。

注意

源端有多少個分片,cluster_helper.py 就會起多少個 redis-shake 進程,所以如果源端分片數(shù)較多的時候,需要評估當前機器是否可以承擔這么多進程。

cluster_helper.py 異常退出的時候,可能沒有正常退出 redis-shake 進程,需要 ps aux | grep redis-shake 檢查。

每個 redis-shake 進程的執(zhí)行日志記錄在 RedisShake/cluster_helper/data/xxxxx 中,反饋問題請?zhí)峁┫嚓P(guān)日志。

依賴

Python 需要 python3.6 及以上版本,安裝 Python依賴:

cd RedisShake/cluster_helper
pip3 install -r requirements.txt
配置

修改 sync.toml:

type = "sync"

[source]
address = "192.168.0.1:6379" # 集群 C 中任意一個節(jié)點地址
password = "r-ccccc:xxxxx"

[target]
type = "cluster"
address = "192.168.1.1:6380" # 集群 D 中任意一個節(jié)點地址
password = "r-ddddd:xxxxx"
運行
cd RedisShake/cluster_helper
python3 cluster_helper.py ../redis-shake ../sync.toml
  • 參數(shù) 1 是 redis-shake 可執(zhí)行程序的路徑
  • 參數(shù) 2 是配置文件路徑
查看redis-shake的日志信息
[root@redis ~]# redis-shake ./redis-shake.toml
2022-08-26 11:20:28 INF GOOS: linux, GOARCH: amd64
2022-08-26 11:20:28 INF Ncpu: 3, GOMAXPROCS: 3
2022-08-26 11:20:28 INF pid: 21504
2022-08-26 11:20:28 INF pprof_port: 0
2022-08-26 11:20:28 INF No lua file specified, will not filter any cmd.
2022-08-26 11:20:28 INF no password. address=[127.0.0.1:6380]
2022-08-26 11:20:28 INF redisWriter connected to redis successful. address=[127.0.0.1:6380]
2022-08-26 11:20:28 INF no password. address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF psyncReader connected to redis successful. address=[127.0.0.1:6379]
2022-08-26 11:20:28 WRN remove file. filename=[4200.aof]
2022-08-26 11:20:28 WRN remove file. filename=[dump.rdb]
2022-08-26 11:20:28 INF start save RDB. address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF send [replconf listening-port 10007]
2022-08-26 11:20:28 INF send [PSYNC ? -1]
2022-08-26 11:20:28 INF receive [FULLRESYNC 1db7c7618b6d0af25ffafb1645d4fba573624d02 0]
2022-08-26 11:20:28 INF source db is doing bgsave. address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF source db bgsave finished. timeUsed=[0.09]s, address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF received rdb length. length=[194]
2022-08-26 11:20:28 INF create dump.rdb file. filename_path=[dump.rdb]
2022-08-26 11:20:28 INF save RDB finished. address=[127.0.0.1:6379], total_bytes=[194]
2022-08-26 11:20:28 INF start send RDB. address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF RDB version: 8
2022-08-26 11:20:28 INF RDB AUX fields. key=[redis-ver], value=[4.0.14]
2022-08-26 11:20:28 INF RDB AUX fields. key=[redis-bits], value=[64]
2022-08-26 11:20:28 INF RDB AUX fields. key=[ctime], value=[1661484028]
2022-08-26 11:20:28 INF RDB AUX fields. key=[used-mem], value=[1897096]
2022-08-26 11:20:28 INF RDB repl-stream-db: 0
2022-08-26 11:20:28 INF RDB AUX fields. key=[repl-id], value=[1db7c7618b6d0af25ffafb1645d4fba573624d02]
2022-08-26 11:20:28 INF RDB AUX fields. key=[repl-offset], value=[0]
2022-08-26 11:20:28 INF RDB AUX fields. key=[aof-preamble], value=[0]
2022-08-26 11:20:28 INF RDB resize db. db_size=[1], expire_size=[0]
2022-08-26 11:20:28 INF send RDB finished. address=[127.0.0.1:6379], repl-stream-db=[0]
2022-08-26 11:20:28 INF start save AOF. address=[127.0.0.1:6379]
2022-08-26 11:20:28 INF AOFWriter open file. filename=[0.aof]
2022-08-26 11:20:29 INF AOFReader open file. aof_filename=[0.aof]
2022-08-26 11:20:33 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[0], aofAppliedOffset=[0]
2022-08-26 11:20:38 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[1], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[14], aofAppliedOffset=[14]
2022-08-26 11:20:43 INF syncing aof. allowOps=[0.00], disallowOps=[0.00], entryId=[1], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[14], aofAppliedOffset=[14]
2022-08-26 11:20:48 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[2], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[28], aofAppliedOffset=[28]


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容