设计一个在Linux上运行的GCC C++程序,同时连接三个不同的PostgreSQL实例,其中两个实例中分别有两个数据库中多个Schema的表结构分别与第三实例中两个数据库中多个Schema个结构完全相同,同时复制两个实例中两个数据库中多个Schema里的所有表的数据到第三个实例中两个数据库中多个Schema里,使用以下快速高效的方法,加入异常处理,支持每隔固定时间重试一定次数,每张表的复制运行状态和记录条数,开始结束时间戳,运行时间,以及每个批次的运行状态,开始结束时间戳,运行时间,总记录条数都要到运行日志文件里,日志文件全部放在logs目录下,每天一个.log扩展名的文本文件,文件名带日期,所有运行参数写在json配置文件中。
根据需求,我设计了以下解决方案。由于代码较长,这里分为多个部分说明:
{
"instances": {
"source1": {
"host": "192.168.1.100",
"port": 5432,
"user": "user1",
"password": "pass1"
},
"source2": {
"host": "192.168.1.101",
"port": 5432,
"user": "user2",
"password": "pass2"
},
"target": {
"host": "192.168.1.102",
"port": 5432,
"user": "target_user",
"password": "target_pass"
}
},
"database_mappings": [
{
"source_instance": "source1",
"source_db": "db1",
"schemas": ["schema1", "schema2"],
"target_db": "target_db1"
},
{
"source_instance": "source2",
"source_db": "db2",
"schemas": ["schema3", "schema4"],
"target_db": "target_db2"
}
],
"retry": {
"max_attempts": 3,
"interval_seconds": 60
},
"log_directory": "logs"
}
#include
#include
#include
class DatabaseConnector {
public:
DatabaseConnector(const std::string& host, int port,
const std::string& dbname, const std::string& user,
const std::string& password)
{
std::string conn_str = fmt::format(
"host={} port={} dbname={} user={} password={}",
host, port, dbname, user, password
);
conn_ = std::make_unique<pqxx::connection>(conn_str);
}
pqxx::result query(const std::string& sql) {
pqxx::work txn(*conn_);
auto result = txn.exec(sql);
txn.commit();
return result;
}
// COPY命令高效传输数据
void copy_table(const std::string& source_table,
const std::string& target_table,
std::shared_ptr<DatabaseConnector> target_conn) {
pqxx::work source_txn(*conn_);
std::stringstream buffer;
source_txn.conn().copy_to(source_table, buffer);
source_txn.commit();
pqxx::work target_txn(*target_conn->conn_);
target_txn.conn().copy_from(buffer, target_table);
target_txn.commit();
}
private:
std::unique_ptr<pqxx::connection> conn_;
};
#include
#include
#include
#include
class Logger {
public:
Logger(const std::string& log_dir) {
namespace fs = std::filesystem;
fs::create_directories(log_dir);
auto now = std::chrono::system_clock::now();
auto today = std::chrono::floor<std::chrono::days>(now);
log_file_ = log_dir + "/" +
format_time(today, "%Y-%m-%d") + ".log";
}
void log(const std::string& message) {
std::lock_guard<std::mutex> lock(mtx_);
std::ofstream file(log_file_, std::ios::app);
file << "[" << format_time(std::chrono::system_clock::now())
<< "] " << message << "\n";
}
private:
std::string format_time(auto time_point, const char* fmt = "%T") {
auto in_time_t = std::chrono::system_clock::to_time_t(time_point);
std::stringstream ss;
ss << std::put_time(std::localtime(&in_time_t), fmt);
return ss.str();
}
std::string log_file_;
std::mutex mtx_;
};
#include
#include
#include
struct Config {
// 解析配置的代码
};
void replicate_data(std::shared_ptr<DatabaseConnector> source,
std::shared_ptr<DatabaseConnector> target,
const std::string& schema,
Logger& logger) {
// 获取所有表结构
auto tables = source->query(fmt::format(
"SELECT table_name FROM information_schema.tables "
"WHERE table_schema = '{}' AND table_type = 'BASE TABLE'",
schema
));
for (const auto& row : tables) {
std::string table_name = row[0].as<std::string>();
std::string qualified_name = schema + "." + table_name;
// 记录开始信息
logger.log(fmt::format("START Table: {}", qualified_name));
try {
// 使用移动数据的高效方法
source->copy_table(qualified_name, qualified_name, target);
// 记录成功信息
logger.log(fmt::format("SUCCESS Table: {}", qualified_name));
} catch (const std::exception& e) {
// 异常处理和重试逻辑
logger.log(fmt::format("ERROR Table: {} - {}",
qualified_name, e.what()));
// 重试逻辑实现
}
}
}
int main() {
// 加载配置
Config config = load_config("config.json");
// 初始化连接池
auto source1 = create_connection(config.instances["source1"]);
// 类似初始化其他连接
// 创建日志器
Logger logger(config.log_directory);
// 处理每个数据库映射
for (const auto& mapping : config.database_mappings) {
// 获取源和目标连接
auto source = get_connection(mapping.source_instance);
auto target = get_connection("target");
// 处理每个schema
for (const auto& schema : mapping.schemas) {
replicate_data(source, target, schema, logger);
}
}
return 0;
}
实现要点说明:
编译和运行:
# 编译命令
g++ -std=c++17 -o pg_replicator main.cpp database.cpp logger.cpp -lpqxx -lpq -lfmt
# 运行命令
./pg_replicator --config config.json
建议的扩展改进方向:
这个设计在保证功能完整性的同时,充分考虑了大规模数据复制的性能需求,并通过模块化设计保证了可维护性。异常处理和日志系统为生产环境运行提供了必要保障。