• 【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)


    介绍一个简单的工具,用于将Redis数据从一个redis端点复制到另一个redis端点,基于原始存储库转换为.NET 8:https://github.com/LuBu0505/redis-copy-net8

     

    Redis Copy .NET8

    Redis Copy 控制台工具允许将 Redis 数据从一个 Redis 服务端复制到另一个。

     Note: 不支持redis集群

     

    软件要求

    运行 Redis Copy 工具需要以下软件。它可能会在其他版本上运行.

    • .NET 8
    • VS Code / Visual Studio 2022

    下载源代码

    clone https://github.com/LuBu0505/redis-copy-net8.git

     

    使用方式

    选项 1 -- 使用 AppSetting.json

    将“< ... >”替换为真实的redis端点

    {
      "SourceRedisConnectionString": ":6380,password=,ssl=True,abortConnect=False", //Source Redis ConnectionString
      "DestRedisConnectionString": ":6380,password=,ssl=True,abortConnect=False" //Destination Redis ConnectionString
    }

     

    选项 2 -- 使用命令参数

    复制代码
    redis-copy-net8.exe
    Parameter Description:
      --se           Required. SourceEndpoint *.redis.cache.windows.net
      --sa           Required. Source password
      --sp           (Default: 6380) Source port
      --sssl         (Default: true) Connect Source over ssl
    
      --de           Required. DestinationEndpoint *.redis.cache.windows.net
      --da           Required. Destination Password
      --dp           (Default: 6380) Destination port
      --dssl         (Default: true) Destination Source over ssl
      --help         Display this help screen.
      --version      Display version information.
    
    eg:
    
    redis-copy-net8.exe --se  --sa <******************> --de  --da <******************> 
    复制代码

     

    Redis Copy 工具的工作流程

    第 1 阶段:准备Redis源和目标信息

    • 使用 StackExchange.Redis ConnectionMultiplexer 类,默认创建20个连接。
    • 检查源redis的Used Memory、Keyspace信息
    • 根据Keys数量拆分成更多子任务
    复制代码
                var infoGroup = sourcecon.BasicRetryInfo((conn) => conn.GetServer(conn.GetEndPoints()[0]).Info());
    
                foreach (var info in infoGroup)
                {
                    if (info.Key.Equals("Memory"))
                    {
                        Console.WriteLine($"==\t# {info.Key}");
                        var lists = info.ToList().Where(i => i.Key.Equals("used_memory_human") || i.Key.Equals("maxmemory_human")).ToList();
                        foreach (var list in lists)
                            Console.WriteLine($"==\t  {list.ToString()}");
                    }
    
                    if (info.Key.Equals("Keyspace"))
                    {
                        Console.WriteLine($"==\t# {info.Key}");
                        foreach (var list in info.ToList())
                        {
                            long dbindex, dbkeys = 0;
    
                            long.TryParse(Regex.Match(list.Key, @"\d+\.*\d*").Value, out dbindex);
                            long.TryParse(list.Value.Split(new char[] { ',' })[0].Split(new char[] { '=' })[1], out dbkeys);
    
                            dictdbIdxKeysNum[dbindex] = dbkeys;
    
                            totalKeysSource += dbkeys;
    
                            Console.WriteLine($"==\t  {list.ToString()}");
                        }
                    }
                }
    复制代码

     

    第二阶段:复制

    • 循环执行复制Redis Keys的子任务,SCAN列出所有Keys。
    • 创建更多子任务以使用 StackExchange.Redis bacth 操作进行 TTL,验证Key是否过期,DUMP出Key的byte[]信息
    • 使用批量操作将Key恢复到目标Redis
    • 如果遇到异常,则将Key信息添加到失败队列中。
    • 检查移动的keys的进度,同时检查失败的队列,如果不为空,将重新运行移动任务
    复制代码
     var allkeys = sourcecon.BasicRetryInfo((conn) => conn.GetServer(conn.GetEndPoints()[0]).Keys(dbindex).Skip(skipKeys).Take(takeKeys)).ToArray();
    var sourcedb = sourcecon.GetConection().GetDatabase(dbindex);
     var destdb = destcon.GetConection().GetDatabase(dbindex);
    
     foreach (var keys in SplitKeys(allkeys))
     {
         var rbatch = sourcedb.CreateBatch();
         var ttltask = new List>();
         var dumptask = new Listbyte[]?>>();
         foreach (var key in keys)
         {
             ttltask.Add(rbatch.KeyTimeToLiveAsync(key));
    
             dumptask.Add(rbatch.KeyDumpAsync(key));
         }
         rbatch.Execute();
    
         var ttlResults = Task.WhenAll(ttltask).Result;
         var dumpkResults = Task.WhenAll(dumptask).Result;
    
         //Restore the key to destation DB.
         var destBatch = destdb.CreateBatch();
    
         var i = 0;
         foreach (var key in keys)
         {
             destBatch.KeyRestoreAsync(key, dumpkResults[i], ttlResults[i]);
             i++;
         }
         destBatch.Execute();
    
         //Random select one key to verify in Phase 3. 
         if (keys.Count() > 0)
         {
             int index = RandomNumberGenerator.GetInt32(keys.Count());
             verifiedKeys.Add((dbindex, keys.ElementAt(index).ToString()));
         }
    
    
         lock (lockObject)
         {
             totalKeysCopied += keys.Count();
         }
     }
    复制代码

     

    第三阶段:验证

    • 随机选取某个key, 一个一个的检查他们的值在两个Redis服务器之间是否相同
    复制代码
                foreach (var key in verifiedKeys)
                {
                    try
                    {
                        var sourdump = await sourcecon.BasicRetryInfo(async (sc) => sc.GetDatabase(key.Item1).KeyDumpAsync(key.Item2));
                        var destdump = await destcon.BasicRetryInfo(async (sc) => sc.GetDatabase(key.Item1).KeyDumpAsync(key.Item2));
    
                        if (!sourdump.Result.SequenceEqual(destdump.Result))
                        {
                            Console.Write($"\n");
                            Console.WriteLine($"== {key} Verify Failed");
                        }
                        else
                        {
                            Console.Write($"{key}, ");
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.BackgroundColor = ConsoleColor.Red;
                        Console.WriteLine($"=={DateTime.Now.ToLocalTime()} Verify {key} failed ({ex.Message})");
                        Console.BackgroundColor = ConsoleColor.Black;
                    }
                }
    复制代码

     

    测试结果

    Copied 369886 keys(812MB) from Redis1 to Redis2 in 233 seconds

     

     

     

  • 相关阅读:
    Latex语法学习09:如何编写一本书
    数据库系统原理与应用教程(081)—— MySQL 视图(View)的创建与使用
    React_lazy使用-组件加载前loading做优雅降级
    Word控件Spire.Doc 【文本】教程(15) ;如何在 C#、VB.NET 的组合框中添加、选择和删除项目
    JS之闭包
    理解计算着色器中glsl语言的内置变量
    [2023年度回顾总结]凡是过往,皆为序章
    ssm+vue的软考系统(有报告)。Javaee项目,ssm vue前后端分离项目。
    第九章 APP项目测试(2) 测试工具
    Android JobScheduler介绍
  • 原文地址:https://www.cnblogs.com/lulight/p/18297088