Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例
设计一个在Linux上运行的GCC C++程序,同时连接三个不同的MySQL实例,其中两个实例中分别有两个Schema的表结构分别与第三实例中两个Schema个结构完全相同,同时复制两个实例中两个Schema里的所有表的数据到第三个实例中两个Schema里,使用以下快速高效的方法,加入异常处理,支持每隔固定时间重试一定次数,每张表的复制运行状态和记录条数,开始结束时间戳,运行时间,以及每个批次的运行状态,开始结束时间戳,运行时间,总记录条数都要到运行日志文件里,日志文件全部放在logs目录下,每天一个.log扩展名的文本文件,文件名带日期,所有运行参数写在json配置文件中。
C++解决方案,采用模块化设计并包含详细注释:
该实现结合了C++的高效性和现代C++的特性,同时利用MySQL X DevAPI提供的高性能接口,能够有效处理大规模数据复制需求。
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <vector>
#include <map>
#include <mysqlx/xdevapi.h>
#include <nlohmann/json.hpp>
#include <filesystem>
#include <mutex>using namespace std;
using namespace mysqlx;
using json = nlohmann::json;
namespace fs = filesystem;// 配置结构体
struct Config {struct DBConfig {string host;int port;string user;string password;};DBConfig source1;DBConfig source2;DBConfig target;int max_retries = 3;int retry_interval = 60; // secondsmap<string, vector<string>> schema_mapping;
};// 日志管理器类
class Logger {
private:mutex log_mutex;string log_dir = "logs";string get_current_date() {auto now = chrono::system_clock::now();time_t t = chrono::system_clock::to_time_t(now);tm tm = *localtime(&t);char buffer[11];strftime(buffer, sizeof(buffer), "%Y%m%d", &tm);return string(buffer);}public:Logger() {if (!fs::exists(log_dir)) {fs::create_directory(log_dir);}}void log(const string& message) {lock_guard<mutex> lock(log_mutex);ofstream log_file(log_dir + "/" + get_current_date() + ".log",ios::app);auto now = chrono::system_clock::now();time_t t = chrono::system_clock::to_time_t(now);log_file << put_time(localtime(&t), "%Y-%m-%d %H:%M:%S") << " | " << message << endl;}
};// 数据库操作类
class DBOperator {Config& config;Logger& logger;Session connect_db(const Config::DBConfig& db_conf) {try {return Session(db_conf.host, db_conf.port, db_conf.user, db_conf.password);} catch (const Error& e) {logger.log("Connection error: " + string(e.what()));throw;}}public:DBOperator(Config& cfg, Logger& log) : config(cfg), logger(log) {}void copy_table_data(Schema& source_schema, Schema& target_schema,const string& table_name,int batch_size = 1000) {auto start_time = chrono::system_clock::now();string log_prefix = "Table[" + table_name + "] ";try {Table source_table = source_schema.getTable(table_name);Table target_table = target_schema.getTable(table_name);int total_rows = 0;RowResult res = source_table.select("*").execute();while (const Row row = res.fetchOne()) {vector<Row> batch;for (int i = 0; i < batch_size && row; ++i) {batch.push_back(row);total_rows++;if (i < batch_size-1) row = res.fetchOne();}target_table.insert().rows(batch).execute();}auto end_time = chrono::system_clock::now();auto duration = chrono::duration_cast<chrono::milliseconds>(end_time - start_time);logger.log(log_prefix + "SUCCESS | Rows: " + to_string(total_rows) +" | Duration: " + to_string(duration.count()) + "ms");} catch (const Error& e) {logger.log(log_prefix + "ERROR: " + string(e.what()));throw;}}void copy_schema(Session& source_session, Session& target_session,const string& source_schema_name,const string& target_schema_name,const vector<string>& tables) {Schema source_schema = source_session.getSchema(source_schema_name);Schema target_schema = target_session.getSchema(target_schema_name);for (const auto& table : tables) {for (int attempt = 0; attempt < config.max_retries; ++attempt) {try {copy_table_data(source_schema, target_schema, table);break;} catch (...) {if (attempt == config.max_retries-1) {logger.log("Table[" + table + "] FAILED after " + to_string(config.max_retries) + " attempts");}this_thread::sleep_for(chrono::seconds(config.retry_interval));}}}}
};int main(int argc, char** argv) {try {// 加载配置文件ifstream config_file("config.json");json config_json = json::parse(config_file);Config config;config.source1 = config_json["source1"];config.source2 = config_json["source2"];config.target = config_json["target"];config.schema_mapping = config_json["schema_mapping"];Logger logger;DBOperator db_operator(config, logger);// 连接数据库Session source1 = db_operator.connect_db(config.source1);Session source2 = db_operator.connect_db(config.source2);Session target = db_operator.connect_db(config.target);// 执行复制任务for (const auto& [source_schema, target_schema] : config.schema_mapping) {vector<string> tables = config.schema_mapping[source_schema];// 从第一个源复制db_operator.copy_schema(source1, target, source_schema, target_schema, tables);// 从第二个源复制db_operator.copy_schema(source2, target, source_schema, target_schema, tables);}} catch (const exception& e) {cerr << "Fatal error: " << e.what() << endl;return 1;}return 0;
}
配置文件示例 (config.json):
{"source1": {"host": "source1.example.com","port": 3306,"user": "user","password": "password"},"source2": {"host": "source2.example.com","port": 3307,"user": "user","password": "password"},"target": {"host": "target.example.com","port": 3308,"user": "user","password": "password"},"max_retries": 3,"retry_interval": 60,"schema_mapping": {"source_schema1": ["table1", "table2"],"source_schema2": ["table3", "table4"]}
}
关键特性说明:
- 配置管理:
- 使用JSON配置文件存储数据库连接参数和复制参数
- 支持灵活的模式映射配置
- 可配置重试策略和批量大小
- 日志系统:
- 自动创建日志目录
- 按天滚动日志文件
- 详细记录每个操作的元数据
- 线程安全的日志写入
- 数据复制:
- 使用MySQL X DevAPI进行高效数据操作
- 批量插入机制提升性能
- 自动重试机制和错误隔离
- 表级并行复制(可扩展)
- 异常处理:
- 多级异常捕获机制
- 连接失败自动重试
- 数据操作错误隔离
- 资源自动释放保障
- 扩展性:
- 模块化设计便于功能扩展
- 支持动态表列表配置
- 可扩展的多线程支持
编译运行:
# 安装依赖
sudo apt-get install libmysqlcppconn-dev nlohmann-json3-dev# 编译
g++ -std=c++17 -o db_sync main.cpp -lmysqlcppconn8 -lpthread# 运行
./db_sync
建议的优化扩展方向:
- 并行处理:
- 使用线程池实现表级并行复制
- 异步IO提升吞吐量
- 连接池管理数据库连接
- 监控增强:
- 实时进度报告
- Prometheus指标导出
- 健康检查端点
- 高级功能:
- 增量复制支持
- 模式校验机制
- 数据一致性校验
- 自动断点续传
- 部署优化:
- Docker容器化封装
- Systemd服务集成
- 配置热加载支持
相关文章:
Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例
设计一个在Linux上运行的GCC C程序,同时连接三个不同的MySQL实例,其中两个实例中分别有两个Schema的表结构分别与第三实例中两个Schema个结构完全相同,同时复制两个实例中两个Schema里的所有表的数据到第三个实例中两个Schema里,使…...
RabbitMQ系列(一)架构解析
RabbitMQ 架构解析 RabbitMQ 是一个基于 AMQP 协议的开源消息中间件,其核心架构通过多组件协作实现高效、可靠的消息传递。以下是其核心组件与协作流程的详细说明: 一、核心组件与功能 Broker(消息代理服务器) RabbitMQ 服务端核…...
XSL 语言:XML 样式表的语言基础与应用
XSL 语言:XML 样式表的语言基础与应用 引言 XSL(Extensible Stylesheet Language)是一种专门用于XML文档样式的语言,它允许用户定义XML文档的格式、布局和外观。XSL是XML技术家族中的重要组成部分,与XML和XPATH等语言共同构成了处理和格式化XML文档的强大工具集。本文将…...
【计算机网络】常见tcp/udp对应的应用层协议,端口
TCP 和 UDP 对应的常见应用层协议 📌 基于 TCP 的应用层协议 协议全称用途默认端口HTTPHyperText Transfer Protocol超文本传输协议80HTTPSHTTP Secure加密的超文本传输协议443FTPFile Transfer Protocol文件传输协议(20 传输数据,21 控制连…...
ExpMoveFreeHandles函数分析和备用空闲表的关系
第一部分:ExpMoveFreeHandles和备用空闲表的关系 ULONG ExpMoveFreeHandles ( IN PHANDLE_TABLE HandleTable ) { ULONG OldValue, NewValue; ULONG Index, OldIndex, NewIndex, FreeSize; PHANDLE_TABLE_ENTRY Entry, FirstEntry; EXHAND…...
微服务学习(1):RabbitMQ的安装与简单应用
目录 RabbitMQ是什么 为什么要使用RabbitMQ RabbitMQ的安装 RabbitMQ架构及其对应概念 队列的主要作用 交换机的主要作用 RabbitMQ的应用 通过控制面板操作(实现收发消息) RabbitMQ是什么 RabbitMQ是一个开源的消息队列软件(消息代理…...
基于javaweb的SSM+Maven幼儿园管理系统设计和实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
企业级本地知识库部署指南(Windows优化版)
一、环境准备 1. 系统优化 # 启用WSL2(需Windows 10 2004或Windows 11) dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform …...
5. Nginx 负载均衡配置案例(附有详细截图说明++)
5. Nginx 负载均衡配置案例(附有详细截图说明) 文章目录 5. Nginx 负载均衡配置案例(附有详细截图说明)1. Nginx 负载均衡 配置实例3. 注意事项和避免的坑4. 文档: Nginx 的 upstream 配置技巧5. 最后: 1. Nginx 负载均衡 配置实例 需求说明/图解 windows 浏览器输…...
Redis---缓存穿透,雪崩,击穿
文章目录 缓存穿透什么是缓存穿透?缓存穿透情况的处理流程是怎样的?缓存穿透的解决办法缓存无效 key布隆过滤器 缓存雪崩什么是缓存雪崩?缓存雪崩的解决办法 缓存击穿什么是缓存击穿?缓存击穿的解决办法 区别对比 在如今的开发中&…...
计算机毕业设计SpringBoot+Vue.js人口老龄化社区服务与管理平台 (源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
【异地访问本地DeepSeek】Flask+内网穿透,轻松实现本地DeepSeek的远程访问
写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 前言依赖Flask构建本地网页访问LM Studio 开启网址访问DeepSeek 调用模板Flask 访问本…...
Nacos + Dubbo3 实现微服务的Rpc调用
文章目录 概念整理基本概念概念助记前提RPC与HTTP类比RPC接口类的一些理解 实例代码主体结构父项目公共接口项目提供者项目项目结构POM文件实现配置文件实现公共接口实现程序入口配置启动项目检查是否可以注入到Nacos 消费者项目项目结构POM文件实现配置文件实现注册RPC服务类实…...
散户如何实现自动化交易下单——篇1:体系介绍与获取同花顺资金账户和持仓信息
一、为什么要实现自动化交易 在瞬息万变的金融市场中,越来越多的散户投资者开始尝试构建自己的交易策略:有人通过技术指标捕捉趋势突破,有人利用基本面分析挖掘低估标的,还有人设计出复杂的网格交易或均值回归模型。然而&a…...
基于Electron的应用程序安全测试基础 — 提取和分析.asar文件的案例研究
目录: 4.4. 案例研究 4.4.2. 情况描述 4.4.3. 信息收集 4.4.3.2. 检查隐藏目录(点目录)的可能性 4.4.3.3. 使用 DB Browser for SQLite 打开 .db 文件 4.4.3.4. 寻找加密算法 4.4.3.5. 找到加密算法 4.4.3.6. 理解加密流程 4.4.3.7. 找到“Ke…...
vue中computed方法使用;computed返回函数
文章目录 1.正常使用computed2.使用computed返回可传参的函数 1.正常使用computed 一般我们使用computed返回一个变量字段,这个字段会根据具体的某个变量计算得到 例如 <div>{{num}}--{{num10}}</div>let num ref(1) let num10 computed(()>{ret…...
大语言模型的评测
大语言模型评测是评估这些模型在各种任务和场景下的性能和能力的过程。 能力 1. 基准测试(Benchmarking) GLUE(General Language Understanding Evaluation):包含多个自然语言处理任务,如文本分类、情感分…...
【Vue3】浅谈setup语法糖
Vue3 的 setup 语法糖是通过 <script setup> 标签启用的特性,它是对 Composition API 的进一步封装,旨在简化组件的声明式写法,同时保留 Composition API 的逻辑组织能力。以下是其核心概念和原理分析: 一、<script setu…...
EasyRTC嵌入式WebRTC技术与AI大模型结合:从ICE框架优化到AI推理
实时通信技术在现代社会中扮演着越来越重要的角色,从视频会议到在线教育,再到远程医疗,其应用场景不断拓展。WebRTC作为一项开源项目,为浏览器和移动应用提供了便捷的实时通信能力。而EasyRTC作为基于WebRTC的嵌入式解决方案&…...
如何管理路由器
一、管理路由器的必要性 1、需要修改拨号上网的密码。 2、需要修改WIFI的SSID名字和密码。 3、设置DHCP协议信息。 4、设置IP地址的过滤规则。 5、给某个设备连接设置网络限速。 二、常见的方式 (一)web网页方式 1、计算机用双绞线或者WIFI的方式连接路由器。 2、在计算机中打开…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...
