经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
来源:cnblogs  作者:路边两盏灯  时间:2024/7/11 20:32:01  对本文有异议

介绍一个简单的工具,用于将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

下载源代码

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

 

使用方式

选项 1 -- 使用 AppSetting.json

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

  1. {
  2. "SourceRedisConnectionString": "<source redis name>:6380,password=<your password>,ssl=True,abortConnect=False", //Source Redis ConnectionString
  3. "DestRedisConnectionString": "<Destination redis name>:6380,password=<your password>,ssl=True,abortConnect=False" //Destination Redis ConnectionString
  4. }

 

选项 2 -- 使用命令参数

  1. redis-copy-net8.exe
  2. Parameter Description:
  3. --se Required. SourceEndpoint *.redis.cache.windows.net
  4. --sa Required. Source password
  5. --sp (Default: 6380) Source port
  6. --sssl (Default: true) Connect Source over ssl
  7. --de Required. DestinationEndpoint *.redis.cache.windows.net
  8. --da Required. Destination Password
  9. --dp (Default: 6380) Destination port
  10. --dssl (Default: true) Destination Source over ssl
  11. --help Display this help screen.
  12. --version Display version information.
  13. eg:
  14. redis-copy-net8.exe --se <xxxxxx.redis.cache.chinacloudapi.cn> --sa <******************> --de <xxxxxx.redis.cache.chinacloudapi.cn> --da <******************>

 

Redis Copy 工具的工作流程

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

  • 使用 StackExchange.Redis ConnectionMultiplexer 类,默认创建20个连接。
  • 检查源redis的Used Memory、Keyspace信息
  • 根据Keys数量拆分成更多子任务
  1. var infoGroup = sourcecon.BasicRetryInfo((conn) => conn.GetServer(conn.GetEndPoints()[0]).Info());
  2. foreach (var info in infoGroup)
  3. {
  4. if (info.Key.Equals("Memory"))
  5. {
  6. Console.WriteLine($"==\t# {info.Key}");
  7. var lists = info.ToList().Where(i => i.Key.Equals("used_memory_human") || i.Key.Equals("maxmemory_human")).ToList();
  8. foreach (var list in lists)
  9. Console.WriteLine($"==\t {list.ToString()}");
  10. }
  11. if (info.Key.Equals("Keyspace"))
  12. {
  13. Console.WriteLine($"==\t# {info.Key}");
  14. foreach (var list in info.ToList())
  15. {
  16. long dbindex, dbkeys = 0;
  17. long.TryParse(Regex.Match(list.Key, @"\d+\.*\d*").Value, out dbindex);
  18. long.TryParse(list.Value.Split(new char[] { ',' })[0].Split(new char[] { '=' })[1], out dbkeys);
  19. dictdbIdxKeysNum[dbindex] = dbkeys;
  20. totalKeysSource += dbkeys;
  21. Console.WriteLine($"==\t {list.ToString()}");
  22. }
  23. }
  24. }

 

第二阶段:复制

  • 循环执行复制Redis Keys的子任务,SCAN列出所有Keys。
  • 创建更多子任务以使用 StackExchange.Redis bacth 操作进行 TTL,验证Key是否过期,DUMP出Key的byte[]信息
  • 使用批量操作将Key恢复到目标Redis
  • 如果遇到异常,则将Key信息添加到失败队列中。
  • 检查移动的keys的进度,同时检查失败的队列,如果不为空,将重新运行移动任务
  1. var allkeys = sourcecon.BasicRetryInfo((conn) => conn.GetServer(conn.GetEndPoints()[0]).Keys(dbindex).Skip(skipKeys).Take(takeKeys)).ToArray();
  2. var sourcedb = sourcecon.GetConection().GetDatabase(dbindex);
  3. var destdb = destcon.GetConection().GetDatabase(dbindex);
  4. foreach (var keys in SplitKeys(allkeys))
  5. {
  6. var rbatch = sourcedb.CreateBatch();
  7. var ttltask = new List<Task<TimeSpan?>>();
  8. var dumptask = new List<Task<byte[]?>>();
  9. foreach (var key in keys)
  10. {
  11. ttltask.Add(rbatch.KeyTimeToLiveAsync(key));
  12. dumptask.Add(rbatch.KeyDumpAsync(key));
  13. }
  14. rbatch.Execute();
  15. var ttlResults = Task.WhenAll(ttltask).Result;
  16. var dumpkResults = Task.WhenAll(dumptask).Result;
  17. //Restore the key to destation DB.
  18. var destBatch = destdb.CreateBatch();
  19. var i = 0;
  20. foreach (var key in keys)
  21. {
  22. destBatch.KeyRestoreAsync(key, dumpkResults[i], ttlResults[i]);
  23. i++;
  24. }
  25. destBatch.Execute();
  26. //Random select one key to verify in Phase 3.
  27. if (keys.Count() > 0)
  28. {
  29. int index = RandomNumberGenerator.GetInt32(keys.Count());
  30. verifiedKeys.Add((dbindex, keys.ElementAt<RedisKey>(index).ToString()));
  31. }
  32. lock (lockObject)
  33. {
  34. totalKeysCopied += keys.Count();
  35. }
  36. }

 

第三阶段:验证

  • 随机选取某个key, 一个一个的检查他们的值在两个Redis服务器之间是否相同
  1. foreach (var key in verifiedKeys)
  2. {
  3. try
  4. {
  5. var sourdump = await sourcecon.BasicRetryInfo(async (sc) => sc.GetDatabase(key.Item1).KeyDumpAsync(key.Item2));
  6. var destdump = await destcon.BasicRetryInfo(async (sc) => sc.GetDatabase(key.Item1).KeyDumpAsync(key.Item2));
  7. if (!sourdump.Result.SequenceEqual(destdump.Result))
  8. {
  9. Console.Write($"\n");
  10. Console.WriteLine($"== {key} Verify Failed");
  11. }
  12. else
  13. {
  14. Console.Write($"{key}, ");
  15. }
  16. }
  17. catch (Exception ex)
  18. {
  19. Console.BackgroundColor = ConsoleColor.Red;
  20. Console.WriteLine($"=={DateTime.Now.ToLocalTime()} Verify {key} failed ({ex.Message})");
  21. Console.BackgroundColor = ConsoleColor.Black;
  22. }
  23. }

 

测试结果

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

 

 

 

原文链接:https://www.cnblogs.com/lulight/p/18297088

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号