暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

redis-port支持前缀迁移

程序员升级之路 2020-08-03
237

一、介绍

redis-port是一款redis数据迁移工具,用来将数据从一个redis迁移到另一个redis实例/redis集群中 ,以下是官方地址:


https://github.com/CodisLabs/redis-port



使用也是非常的简单:

    /redis-port sync -f 127.0.0.1:6379 -t 127.0.0.1:6380 -n 8


    上述命令将127.0.0.1:6379这个redis实例的数据迁移到 127.0.0.1:6380 中。


    我们在生产上迁移了多个redis集群的数据,运行非常稳定。


    最近有这么一个场景:只迁移指定前缀的key,因为一个redis集群有好几个应用在用,如果全部都迁,时间太长,占的内存也比较大。


    二、改造过程

    我们先整理下redis-port的工作流程:

    1、伪装一个从,向主redis 发起同步请求;

    2、主redis将当前数据以rdb发送给redis-port;

    3、redis-port解析rdb文件,然后1条1条的向目标redis写入数据;

    4、接收主redis发送过来的增量命令,发往目标redis。 


    所以如果要改造的话,需要修改3和4两处,当然了还得增加参数解析的代码,具体步骤如下:


    1、先修改cmd/flags.go 增加前缀的参数:

      type Flags struct {
         Source, Target string
         
        //过滤前缀,即只迁移这个前缀的key
        SourcePrefixs []string
       
      AofPath string
      TmpFile struct {
      Path string
      Size int64
      }
      ExpireOffset time.Duration
      }


      其中SourcePrefixs为新加的字段,类型为一个字符串数组,因为前缀可能有多个。



      2、修改cmd/flags.go解析参数函数parseFlagsFromArgs

          // 解析 sourcePrefix 前缀
          for _, key := range []string{"--sourcePrefix"} {
            var prefix string
             if s, ok := d[key].(string); ok && s != "" {
              prefix = s
        }


            //通过,分隔
            flags.SourcePrefixs = strings.Split(prefix, ",")
        }



        3、修改cmd/libs.go,增加判断key是否包含指定前缀的函数

          func strHasPrefix(key string, sourcePrefix []string)  bool{
            if len(sourcePrefix) < 1 {
              return  true
            }


            var hasPrefix bool
            hasPrefix = false
            for i := 0; i < len(sourcePrefix); i++{
              if (strings.HasPrefix(key, sourcePrefix[i])) {
                hasPrefix = true
                break
               }
            }


            return hasPrefix
          }


          4、修改cmd/libs.go处理rdb文件的函数genRestoreCommands增加相应逻辑


            //过滤非前缀的key
            if (strHasPrefix(e.Key.String(), sourcePrefix) != true) {
            log.Debug(e.Key.String() + " has no prefix: ignore it")
            return
            }


            doRestoreDBEntry增加sourcePrefix参数

              func doRestoreDBEntry(entryChan <-chan *rdb.DBEntry
              , targetPrefix string, sourcePrefix []string, addr, auth string, on func(e *rdb.DBEntry) bool) {



              5、修改cmd/libs.go回放aof文件函数doRestoreAoflog

                func doRestoreAoflog(reader *bufio2.Reader, targetPrefix string, sourcePrefix []string, addr, auth string, on func(db uint64, cmd string) bool) {


                //省略一些代码


                //如果前缀不为空,则判断是否为这个前缀
                    if (len(r.Array) > 1) {
                      var key= string(r.Array[1].Value)
                      if strHasPrefix(key, sourcePrefix) !true {
                        continue
                      }
                    }


                //后续逻辑处理
                }



                6、在入口cmd/sync.go增加参数声明

                  Options:
                  -n N, --ncpu=N Set runtime.GOMAXPROCS to N.
                  -m MASTER, --master=MASTER The master redis instance ([auth@]host:port).
                     -t TARGET, --target=TARGET        The target redis instance ([auth@]host:port).
                    --sourcePrefix=SP            filter source key use prefix, separte with, .
                  --db=DB Accept db = DB, default is *.
                  --tmpfile=FILE Use FILE to as socket buffer.


                  再make编译下,可以试下效果:

                  开两个redis实例,实例A为6379端口,实例B为6380端口,实例A数据如下:


                  执行redis-sync:


                  看下实例B中的数据:


                  可以看到只有order前缀的数据才迁移过来了。


                  Redis迁移工具redis-port使用&代码分析

                  扩展Redis:增加Redis命令




                  文章转载自程序员升级之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论