通过 python 脚本迁移 Redis 数据
背景
- 需求:需要将的 Redis 数据迁移由云厂商 A 迁移至云厂商 B
- 问题:云版本的 Redis 版本不支持 SYNC、MIGRATE、BGSAVE 等命令,使得许多工具用不了(如 redis-port)
思路
- (1)从 Redis A 获取所有 key,依次导出数据
- (2)将导出的数据加载到 Redis B
实现
示例:将 127.0.0.1:6379 数据迁移至 127.0.0.1:6380
导出脚本 export.py
from optparse import OptionParser
import os
import redis
import logging
import sysmylogger = None
fdata = Nonedef new_logger(logger_name='AppName',level=logging.INFO,to_file=True,log_file_name="app.log",format='[%(asctime)s] %(filename)s:%(lineno)d %(message)s'):if to_file:handler = logging.FileHandler(log_file_name)else:handler = logging.StreamHandler(sys.stdout)handler.setFormatter(logging.Formatter(format))logger = logging.getLogger(logger_name)logger.addHandler(handler)logger.setLevel(level)return loggerdef gen_redis_proto(*args):'''Write out the string in redis protocol so it can be replayed back later'''proto = '*{0}\\r\\n'.format(len(args))for arg in args:proto += '${0}\\r\\n'.format(len(arg))proto += '{0}\\r\\n'.format(arg)return proto# 只取出指定前缀的 key
def match_key(key: str):return key.startswith('normal')def extract(options, fd, logg: logging.Logger):src_r = redis.StrictRedis(host=options.redis_source_url, port=options.redis_source_port, db=options.redis_source_db)all_keys = src_r.keys('*')for key in all_keys:key_type = ''arr = []try:key = key.decode('utf8')if not match_key(key):continuekey_type = src_r.type(key).decode('utf8')if key_type == 'hash':arr.append('HMSET')arr.append(key)for k, v in src_r.hgetall(key).items():arr.append(k.decode('utf8'))arr.append(v.decode('utf8'))elif key_type == 'string':arr.append('SET')arr.append(key)arr.append(src_r.get(key).decode('utf8'))elif key_type == 'set':arr.append('SADD')arr.append(key)arr.extend([v.decode('utf8') for v in src_r.smembers(key)])elif key_type == 'list':arr.append('LPUSH')arr.append(key)arr.extend([v.decode('utf8') for v in src_r.lrange(key, 0, -1)])elif key_type == 'zset':arr.append('ZADD')arr.append(key)for member, score in src_r.zrange(key, 0, -1, withscores=True):arr.append(str(score))arr.append(member.decode('utf8'))else:# TODO 其它的数据类型logg.error('Unsupported key type detected: {}, key: {}'.format(key_type, key))continuefd.write(gen_redis_proto(*arr) + "\n")# 设置 ttlttl = src_r.ttl(key)if ttl != -1:fd.write(gen_redis_proto(*['EXPIRE', key, str(ttl)]) + "\n")except Exception as e:logg.error('Unsupported key type detected: {}, key: {}, error: {}'.format(key_type, key, e.__str__()))if __name__ == '__main__':parser = OptionParser()parser.add_option('-s', '--redis-source-url',action='store',dest='redis_source_url',help='The url of the source redis which is to be cloned [required]')parser.add_option('-p', '--redis-source-port',action='store',dest='redis_source_port',default=6379,type=int,help='The port of the source redis which is to be cloned [required, \default: 6379]')parser.add_option('-n', '--redis-source-db',action='store',dest='redis_source_db',default=0,type=int,help='The db num of the source redis[required, default: 0]')parser.add_option('-o', '--redis-data-output-file-name',action='store',dest='redis_data_output_file_name',default='redis.data',type=str,help='The output file name of the source redis data[required, default: redis.data]')parser.add_option('-l', '--log-file-name',action='store',dest='log_file_name',default='app.log',type=str,help='The log file name[required, default: app.log]')(options, args) = parser.parse_args()if not (options.redis_source_url and options.redis_source_port):parser.error('redis-source-url, redis-source-port are required arguments. Please see help')data_path = options.redis_data_output_file_nameif os.path.exists(data_path):os.remove(data_path)mylogger = new_logger(to_file=True, level=logging.ERROR, log_file_name=options.log_file_name)with open(data_path, 'a+') as fd:extract(options, fd, mylogger)
导出数据
$ python export.py -s 127.0.0.1 -p 6379$ head redis.data
*5\r\n$5\r\nLPUSH\r\n$11\r\nnormalQueue\r\n$3\r\nccc\r\n$2\r\nbb\r\n$3\r\naaa\r\n
*8\r\n$4\r\nSADD\r\n$9\r\nnormalSet\r\n$2\r\ndd\r\n$2\r\nbb\r\n$2\r\ncc\r\n$2\r\nee\r\n$2\r\naa\r\n$2\r\nff\r\n
*3\r\n$6\r\nEXPIRE\r\n$9\r\nnormalSet\r\n$4\r\n1728\r\n
*6\r\n$5\r\nHMSET\r\n$10\r\nnormalHash\r\n$2\r\nk1\r\n$2\r\nv1\r\n$2\r\nk2\r\n$2\r\nv2\r\n
*3\r\n$3\r\nSET\r\n$9\r\nnormalStr\r\n$3\r\nvvv\r\n
导入脚本 load.sh
#!/bin/shwhile read -r line
donohup printf "%b" "$line"| redis-cli -p 6380 --pipe >> load-std.log 2>> load-err.log &
done < $1
导入数据
sh load.sh redis.data
参考
- https://stackoverflow.com/questions/44288974/sync-with-master-failed-err-unknown-command-sync
- https://gist.github.com/jimmyislive/0efd7a6a1c7f7afd73e8#file-clone_redis-py
相关文章:
通过 python 脚本迁移 Redis 数据
背景 需求:需要将的 Redis 数据迁移由云厂商 A 迁移至云厂商 B问题:云版本的 Redis 版本不支持 SYNC、MIGRATE、BGSAVE 等命令,使得许多工具用不了(如 redis-port) 思路 (1)从 Redis A 获取所…...
nodejs之express学习(1)
安装 npm i express使用 // 导入 const express require(express) // 创建应用 const app express() // 创建路由 app.get(/home,(req,res)>{res.end("hello express") }) app.listen(3000,()>{console.log("服务已启动~") })路由的介绍 什么是…...
【LeetCode】121. 买卖股票的最佳时机
121. 买卖股票的最佳时机 难度:简单 题目 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获…...
Vue3-VueRouter4路由语法解析
1.创建路由实例由createRouter实现 2.路由模式 1)history模式使用createWebHistory():地址栏不带# 2)hash模式使用createWebHashHistory():地址栏带# 3)参数是基础路径,默认/ 括号里的就是设置路径的前…...
ChromeDriver最新版本下载与安装方法
关于ChromeDriver最新下载地址:https://googlechromelabs.github.io/chrome-for-testing/ 下载与安装 setp1:查看Chrome浏览器版本 首先,需要检查Chrome浏览器的版本。请按照以下步骤进行: 打开Chrome浏览器。 点击浏览器右上角…...
illuminate/database 使用 四
文档:Hyperf Database: Getting Started - Laravel 10.x - The PHP Framework For Web Artisans 因为hyperf使用illuminate/database,所以按照文章,看illuminate/database代码实现。 一、读写分离 根据文档读写的host可以分开。设置读写分…...
Spring面向切面编程(AOP);Spring控制反转(IOC);解释一下Spring AOP里面的几个名词;Spring 的 IoC支持哪些功能
文章目录 Spring面向切面编程(AOP)什么是AOPSpring AOP and AspectJ AOP 的区别?Spring AOP中的动态代理如何理解 Spring 中的代理?解释一下Spring AOP里面的几个名词Spring在运行时通知对象Spring切面可以应用5种类型的通知:什么是切面 Aspe…...
vatee万腾的科技征途:Vatee独特探索的数字化力量
在数字化时代的浪潮中,Vatee万腾以其独特的科技征途成为引领者。公司在数字化领域的探索之路不仅是技术的创新,更是一种对未知的勇敢涉足,是对新时代的深刻洞察和积极实践。 Vatee万腾通过独特的探索,展示了在数字化征途上的创新力…...
MySQL学习day03
一、SQL图形化界面工具 常用比较常用的图形化界面有sqlyog、mavicat、datagrip datagrip工具使用相当方便,功能比前面两种都要强大。 DataGrip工具的安装和使用请查看这篇文档:DataGrip 安装教程 DML-介绍 DML全称是Data Manipulation Language(数据…...
《QT从基础到进阶·三十七》QWidget实现左侧导航栏效果
NavigationBarPlugin插件类实现了对左侧导航栏的管理,我们可以在导航栏插件中添加界面,并用鼠标点击导航栏能够切换对应的界面。 源码在文章末尾 实现效果如下: NavigationBarPlugin实现的接口如下: class NAVIGATIONBAR_EXP…...
sftp学习
什么是sftp? sftp的简单操作 远程连接 int RobostSftp::Init(QString ip,QString port,QString user_name, QString user_password) { int rc;session ssh_new();if (!session) {fprintf(stderr, "ssh initialization failed\n");// return 1…...
C++之STL库:string类(用法列举和总结)
前言 大家在学习STL库的时候一定要学会看英文文档,俗话说熟能生巧,所以还得多练!在使用string类之前,要包含头文件#include <string>和using namespace std; 文档链接:string - C Reference 一、string——构造…...
小程序中的大道理--综述
前言 以下将用一个小程序来探讨一些大道理, 这些大道理包括可扩展性, 抽象与封装, 可维护性, 健壮性, 团队合作, 工具的利用, 可测试性, 自顶向下, 分而治之, 分层, 可读性, 模块化, 松耦合, MVC, 领域模型, 甚至对称性, 香农的信息论等等. 为什么不用大程序来说大道理呢? …...
tlais智能学习辅助系统-修改部门功能实现
学习黑马程序员的JavaWeb课程,自己写的部门信息修改部分程序 控制层: //DeptController.java /** * 根据ID查询部门信息 * param id * return */ GetMapping("/{id}") public Result select(PathVariable Integer id){log.info("查询id…...
GLM: 自回归空白填充的多任务预训练语言模型
当前,ChatGLM-6B 在自然语言处理领域日益流行。其卓越的技术特点和强大的语言建模能力使其成为对话语言模型中的佼佼者。让我们深入了解 ChatGLM-6B 的技术特点,探索它在对话模型中的创新之处。 GLM: 自回归空白填充的多任务预训练语言模型 ChatGLM-6B 技…...
函数递归所应满足的条件
1.递归的概念 递归是学习C语⾔函数绕不开的⼀个话题,那什么是递归呢? 递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。 递归的思想: 把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但…...
Python入职某新员工大量使用Lambda表达式,却被老员工喷是屎山
Python中Lambda表达式是一种简洁而强大的特性,其在开发中的使用优缺点明显,需要根据具体场景权衡取舍。 Lambda表达式的优点之一是它的紧凑语法,适用于一些短小而简单的函数。这种形式使得代码更为精炼,特别在一些函数式编程场景中,Lambda表达式可以提高代码的表达力。此外…...
Android Bitmap保存成至手机图片文件,Kotlin
Android Bitmap保存成至手机图片文件,Kotlin fun saveBitmap(name: String?, bm: Bitmap) {val savePath Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()if (!Files.exists(Paths.get(savePath))) {Log.d("保存文…...
frp V0.52.3 搭建
下载 https://github.com/fatedier/frp/releases/ 此版本暂时没有windows的,想在windows使用请下载v0.52.2 简易搭建 frps.toml的配置文件,以下12000、8500需要在云服务器中的防火墙中开放tcp # bindPort为frps和frpc通信的端口,需要在防…...
最近数据分析面试的一点感悟...
我是阿粥,也是小z 最近面了不少应届的同学(数据分析岗位),颇有感触,与各位分享。 简历可以润色,但要适度 运用一些原则,如STAR法则,让简历逻辑更清晰,条块分明࿰…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
