Skip to content

Commit 7ad490d

Browse files
committed
初始化
1 parent 7d09126 commit 7ad490d

13 files changed

+942
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
.idea/.gitignore
3+
.idea/misc.xml
4+
.idea/modules.xml
5+
.idea/redis-migrate.iml
6+
.idea/vcs.xml

Dockerfile

Whitespace-only changes.

Makefile

Whitespace-only changes.

cmd/main.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @Time: 2020/8/8 16:55
3+
* @Author: [email protected]
4+
* @File: main
5+
* @Software: GoLand
6+
*/
7+
8+
package main
9+
10+
import "github.com/icowan/redis-migrate/cmd/migrate"
11+
12+
func main() {
13+
migrate.Run()
14+
}

cmd/migrate/migrate.go

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* @Time: 2020/8/8 12:43
3+
* @Author: [email protected]
4+
* @File: main
5+
* @Software: GoLand
6+
*/
7+
8+
package migrate
9+
10+
import (
11+
"flag"
12+
"os"
13+
14+
"github.com/pkg/errors"
15+
"github.com/spf13/cobra"
16+
17+
"github.com/icowan/redis-migrate/redis"
18+
)
19+
20+
var (
21+
sourceHosts, targetHosts, sourceAuth, targetAuth, sourcePrefix, targetPrefix string
22+
err error
23+
sourceRedisCluster, targetRedisCluster bool
24+
25+
sourceDatabase, targetDatabase int
26+
27+
sourceRedis redis.RedisInterface
28+
targetRedis redis.RedisInterface
29+
30+
rootCmd = &cobra.Command{
31+
Use: "redis-migrate",
32+
Short: "",
33+
SilenceErrors: true,
34+
DisableAutoGenTag: true,
35+
Long: `# redis 迁移工具
36+
可用的配置类型:
37+
[migrate]
38+
有关本系统的相关概述,请参阅 http://gitlab.creditease.corp/yqz/invite-admin
39+
`,
40+
}
41+
42+
migrateRedisCmd = &cobra.Command{
43+
Use: `migrate command <args> [flags]`,
44+
Short: "redis 迁移命令",
45+
SilenceErrors: false,
46+
DisableAutoGenTag: false,
47+
Example: `
48+
支持命令:
49+
[hash, set, sorted-set]
50+
redis-migrate migrate -h
51+
`,
52+
}
53+
)
54+
55+
func init() {
56+
57+
migrateRedisCmd.PersistentFlags().StringVar(&sourceHosts, "source-hosts", "127.0.0.1:6379", "源redis地址, 多个ip用','隔开")
58+
migrateRedisCmd.PersistentFlags().StringVar(&targetHosts, "target-hosts", "127.0.0.1:6379", "目标redis地址, 多个ip用','隔开")
59+
migrateRedisCmd.PersistentFlags().IntVar(&sourceDatabase, "source-database", 0, "源database")
60+
migrateRedisCmd.PersistentFlags().IntVar(&targetDatabase, "target-database", 0, "目标database")
61+
migrateRedisCmd.PersistentFlags().StringVar(&sourceAuth, "source-auth", "", "源密码")
62+
migrateRedisCmd.PersistentFlags().StringVar(&targetAuth, "target-auth", "", "目标密码")
63+
migrateRedisCmd.PersistentFlags().BoolVar(&sourceRedisCluster, "source-redis-cluster", false, "源redis是否是集群")
64+
migrateRedisCmd.PersistentFlags().BoolVar(&targetRedisCluster, "target-redis-cluster", false, "目标redis是否是集群")
65+
migrateRedisCmd.PersistentFlags().StringVar(&sourcePrefix, "source-prefix", "", "源redis前缀")
66+
migrateRedisCmd.PersistentFlags().StringVar(&targetPrefix, "target-prefix", "", "目标redis前缀")
67+
68+
migrateRedisCmd.AddCommand(migrateRedisHashCmd, migrateRedisSortedSetCmd, migrateRedisSetCmd)
69+
addFlags(rootCmd)
70+
rootCmd.AddCommand(migrateRedisCmd)
71+
}
72+
73+
func addFlags(rootCmd *cobra.Command) {
74+
flag.CommandLine.VisitAll(func(gf *flag.Flag) {
75+
rootCmd.PersistentFlags().AddGoFlag(gf)
76+
})
77+
}
78+
79+
func Run() {
80+
if err := rootCmd.Execute(); err != nil {
81+
os.Exit(-1)
82+
}
83+
}
84+
85+
func prepare() error {
86+
var sourceDrive, targetDrive = redis.RedisSingle, redis.RedisSingle
87+
if sourceRedisCluster {
88+
sourceDrive = redis.RedisCluster
89+
}
90+
91+
sourceRedis, err = redis.NewRedisClient(sourceDrive, sourceHosts, sourceAuth, sourcePrefix, sourceDatabase)
92+
if err != nil {
93+
return errors.Wrap(err, "源redis连接失败!")
94+
}
95+
96+
if targetRedisCluster {
97+
targetDrive = redis.RedisCluster
98+
}
99+
100+
targetRedis, err = redis.NewRedisClient(targetDrive, targetHosts, targetAuth, targetPrefix, targetDatabase)
101+
if err != nil {
102+
return errors.Wrap(err, "目标redis连接失败!")
103+
}
104+
return nil
105+
}
106+
107+
func getS(n int, char string) (s string) {
108+
for i := 1; i <= n; i++ {
109+
s += char
110+
}
111+
return
112+
}

cmd/migrate/migrate_hash.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @Time: 2020/8/8 16:35
3+
* @Author: [email protected]
4+
* @File: hash
5+
* @Software: GoLand
6+
*/
7+
8+
package migrate
9+
10+
import (
11+
"fmt"
12+
"log"
13+
"math"
14+
"os"
15+
"time"
16+
17+
"github.com/pkg/errors"
18+
"github.com/spf13/cobra"
19+
)
20+
21+
var (
22+
migrateRedisHashCmd = &cobra.Command{
23+
Use: `hash <args> [flags]`,
24+
Short: "哈希列表",
25+
SilenceErrors: false,
26+
DisableAutoGenTag: false,
27+
Example: `
28+
redis-tool migrate hash {key} --source-redis-cluster true --source-hosts 127.0.0.1:6379,127.0.0.1:7379 --source-auth 123456 --target-redis-cluster true --target-hosts 127.0.0.1:6379,127.0.0.1:7379 --target-auth 123456
29+
`,
30+
RunE: func(cmd *cobra.Command, args []string) error {
31+
// 关闭资源连接
32+
defer func() {
33+
log.Printf("source redis close err: %v", sourceRedis.Close())
34+
log.Printf("target redis close err: %v", targetRedis.Close())
35+
}()
36+
if len(args) < 1 {
37+
fmt.Println("至少需要一个参数")
38+
return errors.New("参数错误")
39+
}
40+
return migrateRedisHGetAll(args[0])
41+
},
42+
PreRunE: func(cmd *cobra.Command, args []string) error {
43+
if err = prepare(); err != nil {
44+
fmt.Println(fmt.Sprintf("prepare error: %s", err.Error()))
45+
return err
46+
}
47+
return nil
48+
},
49+
}
50+
)
51+
52+
func migrateRedisHGetAll(key string) error {
53+
begin := time.Now()
54+
55+
total, err := sourceRedis.HLen(key)
56+
if err != nil {
57+
err = errors.Wrap(err, "sourceRedis.Hlen")
58+
return err
59+
}
60+
61+
fmt.Println(fmt.Sprintf("Key: [%s] 总数: [%d]", key, total))
62+
63+
res, err := sourceRedis.HGetAll(key)
64+
if err != nil {
65+
err = errors.Wrap(err, "sourceRedis.HGetAll")
66+
return err
67+
}
68+
69+
step := math.Ceil(100 / float64(total))
70+
var i = 1
71+
for k, v := range res {
72+
i += int(step)
73+
if i >= 100-int(step) {
74+
i = 100
75+
}
76+
_, _ = fmt.Fprintf(os.Stdout, "%d%% [%s]\r", i, getS(i, "#")+getS(100-i, " "))
77+
if err = targetRedis.HSet(key, k, v); err != nil {
78+
continue
79+
}
80+
if i == 100 {
81+
fmt.Println(fmt.Sprintf("%d%% [%s]\r", i, getS(i, "#")+getS(100-i, " ")))
82+
}
83+
}
84+
85+
fmt.Println(fmt.Sprintf("迁移完成, 用时 [%v]", time.Since(begin)))
86+
87+
return nil
88+
}

cmd/migrate/migrate_set.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @Time: 2020/8/8 16:38
3+
* @Author: [email protected]
4+
* @File: set
5+
* @Software: GoLand
6+
*/
7+
8+
package migrate
9+
10+
import (
11+
"fmt"
12+
"log"
13+
"time"
14+
15+
"github.com/pkg/errors"
16+
"github.com/spf13/cobra"
17+
)
18+
19+
var (
20+
migrateRedisSetCmd = &cobra.Command{
21+
Use: `set <args> [flags]`,
22+
Short: "redis set",
23+
SilenceErrors: false,
24+
DisableAutoGenTag: false,
25+
Example: `
26+
redis-tool migrate set {key} --source-redis-cluster true --source-hosts 127.0.0.1:6379,127.0.0.1:7379 --source-auth 123456 --target-redis-cluster true --target-hosts 127.0.0.1:6379,127.0.0.1:7379 --target-auth 123456
27+
`,
28+
RunE: func(cmd *cobra.Command, args []string) error {
29+
// 关闭资源连接
30+
defer func() {
31+
log.Printf("source redis close err: %v", sourceRedis.Close())
32+
log.Printf("target redis close err: %v", targetRedis.Close())
33+
}()
34+
if len(args) < 1 {
35+
fmt.Println("至少需要一个参数")
36+
return errors.New("参数错误")
37+
}
38+
return migrateRedisSet(args[0])
39+
},
40+
PreRunE: func(cmd *cobra.Command, args []string) error {
41+
if err = prepare(); err != nil {
42+
fmt.Println(fmt.Sprintf("prepare error: %s", err.Error()))
43+
return err
44+
}
45+
return nil
46+
},
47+
}
48+
)
49+
50+
func migrateRedisSet(key string) error {
51+
begin := time.Now()
52+
53+
res, err := sourceRedis.Keys(key)
54+
if err != nil {
55+
err = errors.Wrap(err, "sourceRedis.Keys")
56+
return err
57+
}
58+
59+
fmt.Println(fmt.Sprintf("Key: [%s] 总数: [%d]", key, len(res)))
60+
61+
for _, val := range res {
62+
v, err := sourceRedis.Get(val)
63+
if err != nil || v == "" {
64+
fmt.Println(fmt.Sprintf("迁移: [%s] --> failure: %s", val, "key不存在"))
65+
continue
66+
}
67+
68+
if err = targetRedis.Set(val, v, sourceRedis.TTL(val)); err != nil {
69+
fmt.Println(fmt.Sprintf("迁移: [%s] --> failure: %s", val, err.Error()))
70+
continue
71+
}
72+
}
73+
fmt.Println(fmt.Sprintf("迁移完成, 用时 [%v]", time.Since(begin)))
74+
75+
return nil
76+
}

cmd/migrate/migrate_sorted-set.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @Time: 2020/8/8 16:40
3+
* @Author: [email protected]
4+
* @File: sorted-set
5+
* @Software: GoLand
6+
*/
7+
8+
package migrate
9+
10+
import (
11+
"fmt"
12+
"log"
13+
"math"
14+
"os"
15+
"time"
16+
17+
"github.com/pkg/errors"
18+
"github.com/spf13/cobra"
19+
)
20+
21+
var (
22+
migrateRedisSortedSetCmd = &cobra.Command{
23+
Use: `sorted-set <args> [flags]`,
24+
Short: "有序集合",
25+
SilenceErrors: false,
26+
DisableAutoGenTag: false,
27+
Example: `
28+
redis-tool migrate sorted-set {key} --source-redis-cluster true --source-hosts 127.0.0.1:6379,127.0.0.1:7379 --source-auth 123456 --target-redis-cluster true --target-hosts 127.0.0.1:6379,127.0.0.1:7379 --target-auth 123456
29+
`,
30+
RunE: func(cmd *cobra.Command, args []string) error {
31+
// 关闭资源连接
32+
defer func() {
33+
log.Printf("source redis close err: %v", sourceRedis.Close())
34+
log.Printf("target redis close err: %v", targetRedis.Close())
35+
}()
36+
if len(args) < 1 {
37+
fmt.Println("至少需要一个参数")
38+
return errors.New("参数错误")
39+
}
40+
//return readFile()
41+
return migrateRedisSortedSet(args[0])
42+
},
43+
PreRunE: func(cmd *cobra.Command, args []string) error {
44+
if err = prepare(); err != nil {
45+
fmt.Println(fmt.Sprintf("prepare error: %s", err.Error()))
46+
return err
47+
}
48+
return nil
49+
},
50+
}
51+
)
52+
53+
func migrateRedisSortedSet(key string) error {
54+
begin := time.Now()
55+
56+
total, err := sourceRedis.ZCard(key)
57+
if err != nil {
58+
err = errors.Wrap(err, "sourceRedis.ZCard")
59+
return err
60+
}
61+
62+
fmt.Println(fmt.Sprintf("Key: [%s] 总数: [%d]", key, total))
63+
var base float64 = 50000
64+
65+
step := math.Ceil(float64(total) / base)
66+
67+
for n := 0; n < int(step); n++ {
68+
res, err := sourceRedis.ZRangeWithScores(key, int64(n)*50000, int64(n+1)*50000)
69+
if err != nil {
70+
err = errors.Wrap(err, "sourceRedis.ZRangeWithScores")
71+
return err
72+
}
73+
74+
s := 100 / base
75+
var i float64 = 0
76+
for _, v := range res {
77+
i += s
78+
if i >= 100-s {
79+
i = 100
80+
}
81+
_, _ = fmt.Fprintf(os.Stdout, "[%d/%d] %.2f%% \r", n+1, int(step), i)
82+
if err = targetRedis.ZAdd(key, v.Score, v.Member); err != nil {
83+
continue
84+
}
85+
}
86+
fmt.Println(fmt.Sprintf("迁移第: [%d/%d], 用时 [%v]", n+1, int(step), time.Since(begin)))
87+
}
88+
89+
fmt.Println(fmt.Sprintf("迁移完成, 用时 [%v]", time.Since(begin)))
90+
91+
return nil
92+
}

0 commit comments

Comments
 (0)