Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Support AWS sync mode #662

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
2 changes: 1 addition & 1 deletion cmd/redis-shake/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion docs/src/zh/function/best_practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ outline: deep

# 最佳实践


TODO
2 changes: 1 addition & 1 deletion docs/src/zh/function/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ outline: deep

# 什么是 function


TODO

48 changes: 46 additions & 2 deletions docs/src/zh/guide/mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 11 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions internal/log/init.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package log

import (
"RedisShake/internal/config"
"fmt"
"github.com/rs/zerolog"
"os"
Expand All @@ -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":
Expand All @@ -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))
}
Expand Down
2 changes: 1 addition & 1 deletion internal/reader/sync_standalone_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)

Expand Down
2 changes: 1 addition & 1 deletion shake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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