feature: Support AWS sync mode

v4
suxb201 1 year ago committed by suxb201
parent 615341a9fc
commit 96f9724428
  1. 2
      README.md
  2. 2
      cmd/redis-shake/main.go
  3. 2
      docs/src/zh/function/best_practices.md
  4. 2
      docs/src/zh/function/introduction.md
  5. 48
      docs/src/zh/guide/mode.md
  6. 13
      internal/config/config.go
  7. 5
      internal/log/init.go
  8. 2
      internal/reader/sync_standalone_reader.go
  9. 2
      shake.toml

@ -1,6 +1,6 @@
# RedisShake: Redis Data Processing & Migration Tool
[![CI](https://github.com/tair-opensource/RedisShake/actions/workflows/ci.yml/badge.svg?event=push?branch=v4)](https://github.com/tair-opensource/RedisShake/actions/workflows/ci.yml)
[![CI](https://github.com/tair-opensource/RedisShake/actions/workflows/ci.yml/badge.svg?event=push&branch=v4)](https://github.com/tair-opensource/RedisShake/actions/workflows/ci.yml)
[![CI](https://github.com/tair-opensource/RedisShake/actions/workflows/pages.yml/badge.svg?branch=v4)](https://github.com/tair-opensource/RedisShake/actions/workflows/pages.yml)
[![CI](https://github.com/tair-opensource/RedisShake/actions/workflows/release.yml/badge.svg?branch=v4)](https://github.com/tair-opensource/RedisShake/actions/workflows/release.yml)

@ -15,7 +15,7 @@ import (
func main() {
v := config.LoadConfig()
log.Init(config.Opt.Advanced.LogLevel, config.Opt.Advanced.LogFile)
log.Init(config.Opt.Advanced.LogLevel, config.Opt.Advanced.LogFile, config.Opt.Advanced.Dir)
utils.ChdirAndAcquireFileLock()
utils.SetNcpu()
utils.SetPprofPort()

@ -4,4 +4,4 @@ outline: deep
# 最佳实践
TODO

@ -4,5 +4,5 @@ outline: deep
# 什么是 function
TODO

@ -6,6 +6,50 @@ outline: deep
目前 RedisShake 有三种迁移模式:`PSync`、`RDB` 和
`SCAN`,分别对应 [`sync_reader`](../reader/sync_reader.md)、[`rdb_reader`](../reader/rdb_reader.md)
和 [`scan_reader`](../reader/scan_reader.md)。这三种模式各有适合的场景,本文根据场景特点介绍如何选择。
和 [`scan_reader`](../reader/scan_reader.md)。
对于从备份中恢复数据的场景,可以使用 `rdb_reader`
对于数据迁移场景,优先选择 `sync_reader`。一些云厂商没有提供 PSync 协议支持,可以选择`scan_reader`。
对于长期的数据同步场景,RedisShake 目前没有能力承接,因为 PSync 协议并不可靠,当复制连接断开时,RedisShake 将无法重新连接至源端数据库。如果对于可用性要求不高,可以使用 `scan_reader`。如果写入量不大,且不存在大 key,也可以考虑 `scan_reader`
不同模式各有优缺点,需要查看各 Reader 章节了解更多信息。
## 云 Redis 服务
主流云厂商都提供了 Redis 服务,不过有几个原因导致在这些服务上使用 RedisShake 较为复杂:
1. 引擎限制。存在一些自研的 Redis-like 数据库没有兼容 PSync 协议。
2. 架构限制。较多云厂商支持代理模式,即在用户与 Redis 服务之间增加 Proxy 组件。因为 Proxy 组件的存在,所以 PSync 协议无法支持。
3. 安全限制。在原生 Redis 中 PSync 协议基本会触发 fork(2),会导致内存膨胀与用户请求延迟增加,较坏情况下甚至会发生 out of memory。尽管这些都有方案缓解,但并不是所有云厂商都有这方面的投入。
4. 商业策略。较多用户使用 RedisShake 是为了下云或者换云,所以部分云厂商并不希望用户使用 RedisShake,从而屏蔽了 PSync 协议。
下文会结合实践经验,介绍一些特殊场景下的 RedisShake 使用方案。
### 阿里云 Redis 与 Tair
阿里云 Redis 与 Tair 都支持 PSync 协议,推荐使用 `sync_reader`。用户需要创建一个具有复制权限的账号,RedisShake 可以使用该账号进行数据同步,具体创建步骤见 [创建与管理账号](https://help.aliyun.com/zh/redis/user-guide/create-and-manage-database-accounts)。
例外情况:
1. 2.8 版本的 Redis 实例不支持创建复制权限的账号,需要 [升级大版本](https://help.aliyun.com/zh/redis/user-guide/upgrade-the-major-version-1)。
2. 集群架构的 Reids 与 Tair 实例在 [代理模式](https://help.aliyun.com/zh/redis/product-overview/cluster-master-replica-instances#section-h69-izd-531) 下不支持 PSync 协议。
3. 读写分离架构不支持 PSync 协议。
在不支持 PSync 协议的场景下,可以使用 `scan_reader`。需要注意的是,`scan_reader` 会对源库造成较大的压力。
### AWS ElastiCache and MemoryDB
优选 `sync_reader`, AWS ElastiCache and MemoryDB 默认情况下没有开启 PSync 协议,但是可以通过提交工单的方式请求开启 PSync 协议。AWS 会在工单中给出一份重命名的 PSync 命令,比如 `xhma21yfkssync``nmfu2bl5osync`。此命令效果等同于 `psync` 命令,只是名字不一样。
用户修改 RedisShake 配置文件中的 `aws_psync` 配置项即可。对于单实例只写一对 `ip:port@cmd` 即可,对于集群实例,需要写上所有的 `ip:port@cmd`,以逗号分隔。
不方便提交工单时,可以使用 `scan_reader`。需要注意的是,`scan_reader` 会对源库造成较大的压力。
## 自建 Redis 或 Redis-like 数据库
## Redis Sentinel 架构
当 Redis 以 sentinel 架构部署时,RedisShake 通过 PSync 协议连接主库会被认为是 slave,从而有可能被 sentinel 选举为新的 master。为了避免这种情况,应选择备库作为源端。
TODO

@ -1,11 +1,13 @@
package config
import (
"RedisShake/internal/log"
"fmt"
"github.com/mcuadros/go-defaults"
"github.com/rs/zerolog"
"github.com/spf13/viper"
"os"
"strings"
)
type AdvancedOptions struct {
@ -34,11 +36,18 @@ type AdvancedOptions struct {
TargetRedisClientMaxQuerybufLen int64 `mapstructure:"target_redis_client_max_querybuf_len" default:"1024000000"`
TargetRedisProtoMaxBulkLen uint64 `mapstructure:"target_redis_proto_max_bulk_len" default:"512000000"`
AwsPSync string `mapstructure:"aws_psync" default:""` // "ip:port@xxxpsync;ip:port@xxxpsync"
AwsPSync string `mapstructure:"aws_psync" default:""` // 10.0.0.1:6379@nmfu2sl5osync,10.0.0.1:6379@xhma21xfkssync
}
func (opt *AdvancedOptions) GetPSyncCommand(address string) string {
return fmt.Sprintf("psync %s 1 0", address)
items := strings.Split(opt.AwsPSync, ",")
for _, item := range items {
if strings.HasPrefix(item, address) {
return strings.Split(item, "@")[1]
}
}
log.Panicf("can not find aws psync command. address=[%s],aws_psync=[%s]", address, opt.AwsPSync)
return ""
}
type ShakeOptions struct {

@ -1,7 +1,6 @@
package log
import (
"RedisShake/internal/config"
"fmt"
"github.com/rs/zerolog"
"os"
@ -10,7 +9,7 @@ import (
var logger zerolog.Logger
func Init(level string, file string) {
func Init(level string, file string, dir string) {
// log level
switch level {
case "debug":
@ -24,7 +23,7 @@ func Init(level string, file string) {
}
// dir
dir, err := filepath.Abs(config.Opt.Advanced.Dir)
dir, err := filepath.Abs(dir)
if err != nil {
panic(fmt.Sprintf("failed to determine current directory: %v", err))
}

@ -116,7 +116,7 @@ func (r *syncStandaloneReader) sendPSync() {
// send PSync
argv := []string{"PSYNC", "?", "-1"}
if config.Opt.Advanced.AwsPSync != "" {
argv = []string{config.Opt.Advanced.AwsPSync, "?", "-1"} // TODO AWS PSYNC
argv = []string{config.Opt.Advanced.GetPSyncCommand(r.stat.Address), "?", "-1"}
}
r.client.Send(argv...)

@ -59,4 +59,4 @@ target_redis_client_max_querybuf_len = 1024_000_000
target_redis_proto_max_bulk_len = 512_000_000
# If the source is Elasticache or MemoryDB, you can set this item.
aws_psync = ""
aws_psync = "" # example: aws_psync = "10.0.0.1:6379@nmfu2sl5osync,10.0.0.1:6379@xhma21xfkssync"

Loading…
Cancel
Save