从C API到Connector/C++:一个C++算法工程师的MySQL连接库升级心路与配置指南
引言
作为一名长期在算法和数据分析领域耕耘的C++工程师,我经历过无数次与数据库打交道的场景。最初,我像大多数C++开发者一样,选择了MySQL的C API作为与数据库交互的工具。然而,随着项目复杂度增加,那些冗长的代码、繁琐的资源管理和难以调试的错误让我开始怀疑人生——直到发现了Connector/C++这个宝藏。
Connector/C++是MySQL官方提供的C++接口库,它用面向对象的方式封装了底层细节,让数据库操作变得优雅而高效。本文将分享我从C API迁移到Connector/C++的完整历程,包括技术对比、安装配置、代码重构案例以及在算法项目中的最佳实践。无论你是正在忍受C API折磨的开发者,还是刚接触MySQL的C++程序员,这篇文章都能为你提供一条清晰的升级路径。
1. 为什么放弃C API:一个过来人的血泪史
1.1 C API的典型痛点
使用MySQL C API开发时,我经常遇到这些问题:
- 代码冗长:一个简单的查询需要数十行代码
- 手动内存管理:频繁的
mysql_free_result()调用让人提心吊胆 - 错误处理复杂:需要检查每个API调用的返回值
- 类型转换繁琐:从结果集中提取数据需要显式类型转换
// C API的典型查询代码 MYSQL *conn = mysql_init(NULL); if (!mysql_real_connect(conn, "localhost", "user", "password", "db", 0, NULL, 0)) { fprintf(stderr, "连接失败: %s\n", mysql_error(conn)); exit(1); } if (mysql_query(conn, "SELECT id, name FROM users")) { fprintf(stderr, "查询失败: %s\n", mysql_error(conn)); exit(1); } MYSQL_RES *result = mysql_store_result(conn); MYSQL_ROW row; while ((row = mysql_fetch_row(result))) { printf("ID: %s, Name: %s\n", row[0], row[1]); } mysql_free_result(result); mysql_close(conn);1.2 Connector/C++带来的改变
切换到Connector/C++后,同样的功能代码量减少了一半以上,而且更易读、更安全:
#include <mysql_connection.h> #include <mysql_driver.h> #include <cppconn/resultset.h> sql::mysql::MySQL_Driver *driver; sql::Connection *con; driver = sql::mysql::get_mysql_driver_instance(); con = driver->connect("tcp://127.0.0.1:3306", "user", "password"); std::unique_ptr<sql::Statement> stmt(con->createStatement()); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT id, name FROM users")); while (res->next()) { std::cout << "ID: " << res->getInt("id") << ", Name: " << res->getString("name") << std::endl; }关键优势对比:
| 特性 | C API | Connector/C++ |
|---|---|---|
| 代码量 | 冗长 | 简洁 |
| 内存管理 | 手动 | 自动(支持智能指针) |
| 错误处理 | 显式检查返回值 | 异常机制 |
| 类型安全 | 弱 | 强 |
| 面向对象 | 无 | 完整支持 |
| 现代C++特性支持 | 无 | 支持C++11/14/17 |
2. Connector/C++环境搭建指南
2.1 系统准备
在Ubuntu系统上安装Connector/C++有两种方式:
APT安装(推荐):适用于标准Ubuntu版本
sudo apt update sudo apt install libmysqlcppconn-dev源码编译:适用于自定义系统或需要特定功能
2.2 源码编译详细步骤
当预编译包不适用时,源码编译是最可靠的选择。以下是完整流程:
依赖安装:
# 基础编译工具 sudo apt install build-essential cmake # MySQL客户端库 sudo apt install mysql-client libmysqlclient-dev # 可选:如果需要JDBC支持 sudo apt install libboost-dev openssl编译过程:
# 1. 下载源码并解压 wget https://dev.mysql.com/get/Downloads/Connector-C++/mysql-connector-c++-8.0.28.tar.gz tar -xzvf mysql-connector-c++-8.0.28.tar.gz cd mysql-connector-c++-8.0.28 # 2. 创建构建目录 mkdir build && cd build # 3. 配置编译选项 cmake .. -DCMAKE_BUILD_TYPE=Release \ -DWITH_JDBC=ON \ -DWITH_BOOST=/usr/include/boost # 4. 编译安装 make -j$(nproc) sudo make install安装后配置:
# 头文件位置 sudo cp -r /usr/local/mysql/connector-c++-8.0.28/include/* /usr/local/include/ # 库文件位置 sudo cp /usr/local/mysql/connector-c++-8.0.28/lib64/* /usr/lib/提示:如果遇到动态库链接问题,可以在编译应用时添加
-Wl,-allow-shlib-undefined选项,但这不是推荐做法,最好确保所有依赖正确安装。
3. 从C API到Connector/C++:代码重构实战
3.1 连接管理重构
C API方式:
MYSQL *conn = mysql_init(NULL); if (!mysql_real_connect(conn, "localhost", "user", "pwd", "db", 0, NULL, 0)) { // 错误处理 } // 使用后必须记得关闭 mysql_close(conn);Connector/C++方式:
#include <memory> auto driver = sql::mysql::get_mysql_driver_instance(); std::unique_ptr<sql::Connection> conn(driver->connect( "tcp://127.0.0.1:3306", "user", "pwd")); conn->setSchema("db"); // 无需手动关闭,unique_ptr会在析构时自动释放资源3.2 查询操作重构
C API方式:
if (mysql_query(conn, "SELECT * FROM products WHERE price > 100")) { // 错误处理 } MYSQL_RES *result = mysql_store_result(conn); int num_fields = mysql_num_fields(result); MYSQL_ROW row; while ((row = mysql_fetch_row(result))) { for (int i = 0; i < num_fields; i++) { printf("%s ", row[i] ? row[i] : "NULL"); } printf("\n"); } mysql_free_result(result);Connector/C++方式:
auto stmt = conn->createStatement(); auto res = std::unique_ptr<sql::ResultSet>( stmt->executeQuery("SELECT * FROM products WHERE price > 100")); while (res->next()) { std::cout << "ID: " << res->getInt("id") << ", Name: " << res->getString("name") << ", Price: " << res->getDouble("price") << std::endl; }3.3 预处理语句重构
预处理语句是防止SQL注入的最佳实践,Connector/C++提供了更优雅的实现:
C API方式:
MYSQL_STMT *stmt = mysql_stmt_init(conn); const char *query = "INSERT INTO users (name, age) VALUES (?, ?)"; mysql_stmt_prepare(stmt, query, strlen(query)); MYSQL_BIND bind[2]; char name[100] = "John"; unsigned long name_len = strlen(name); int age = 30; memset(bind, 0, sizeof(bind)); bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = name; bind[0].buffer_length = 100; bind[0].length = &name_len; bind[1].buffer_type = MYSQL_TYPE_LONG; bind[1].buffer = &age; mysql_stmt_bind_param(stmt, bind); mysql_stmt_execute(stmt); mysql_stmt_close(stmt);Connector/C++方式:
auto pstmt = std::unique_ptr<sql::PreparedStatement>( conn->prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)")); pstmt->setString(1, "John"); pstmt->setInt(2, 30); pstmt->executeUpdate();4. 在算法项目中集成Connector/C++的最佳实践
4.1 数据库连接池设计
对于需要频繁访问数据库的算法服务,连接池是必备组件。Connector/C++本身不提供连接池,但我们可以轻松实现一个:
class ConnectionPool { public: ConnectionPool(size_t size, const std::string& url, const std::string& user, const std::string& password) : driver_(sql::mysql::get_mysql_driver_instance()) { for (size_t i = 0; i < size; ++i) { auto conn = std::shared_ptr<sql::Connection>( driver_->connect(url, user, password)); pool_.push(conn); } } std::shared_ptr<sql::Connection> getConnection() { std::unique_lock<std::mutex> lock(mutex_); while (pool_.empty()) { cv_.wait(lock); } auto conn = pool_.front(); pool_.pop(); return conn; } void returnConnection(std::shared_ptr<sql::Connection> conn) { std::unique_lock<std::mutex> lock(mutex_); pool_.push(conn); cv_.notify_one(); } private: sql::mysql::MySQL_Driver* driver_; std::queue<std::shared_ptr<sql::Connection>> pool_; std::mutex mutex_; std::condition_variable cv_; };4.2 批量数据处理技巧
算法项目经常需要处理大量数据,Connector/C++的批量操作能显著提升性能:
// 批量插入示例 void batchInsert(const std::vector<User>& users) { auto conn = pool_.getConnection(); conn->setAutoCommit(false); // 关闭自动提交 auto pstmt = conn->prepareStatement( "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"); try { for (const auto& user : users) { pstmt->setString(1, user.name); pstmt->setString(2, user.email); pstmt->setInt(3, user.age); pstmt->addBatch(); // 添加到批处理 } pstmt->executeBatch(); // 执行批处理 conn->commit(); // 提交事务 } catch (...) { conn->rollback(); // 出错回滚 throw; } pool_.returnConnection(conn); }4.3 与算法模块的优雅集成
将数据库操作与算法逻辑分离是保持代码整洁的关键:
class RecommendationSystem { public: RecommendationSystem(std::shared_ptr<ConnectionPool> pool) : pool_(pool) {} std::vector<Product> recommendForUser(int userId) { auto conn = pool_->getConnection(); // 从数据库获取用户数据 auto userData = getUserData(conn, userId); // 获取候选产品 auto candidates = getCandidateProducts(conn, userData); // 运行推荐算法 auto results = runAlgorithm(userData, candidates); pool_->returnConnection(conn); return results; } private: UserData getUserData(sql::Connection* conn, int userId) { // 实现数据获取逻辑 } std::vector<Product> getCandidateProducts(sql::Connection* conn, const UserData& data) { // 实现候选产品查询 } std::vector<Product> runAlgorithm(const UserData& data, const std::vector<Product>& candidates) { // 实现推荐算法 } std::shared_ptr<ConnectionPool> pool_; };4.4 性能优化技巧
- 使用连接池:避免频繁创建和销毁连接
- 合理使用事务:批量操作时开启事务
- 预处理语句重用:不要每次查询都创建新的预处理语句
- 结果集处理:对于大数据集,使用
setFetchSize()控制内存使用 - 连接参数调优:调整
connectTimeout和readTimeout等参数
// 连接参数配置示例 sql::ConnectOptionsMap connection_properties; connection_properties["hostName"] = "localhost"; connection_properties["userName"] = "user"; connection_properties["password"] = "password"; connection_properties["schema"] = "db"; connection_properties["OPT_CONNECT_TIMEOUT"] = 10; connection_properties["OPT_READ_TIMEOUT"] = 30; auto conn = driver->connect(connection_properties);5. 常见问题与解决方案
5.1 编译与链接问题
问题1:找不到mysqlcppconn库
/usr/bin/ld: cannot find -lmysqlcppconn解决方案:
# 确认库文件位置 sudo find / -name "*mysqlcppconn*" # 如果库在非标准路径,添加链接路径 g++ your_program.cpp -L/path/to/libs -lmysqlcppconn问题2:头文件找不到
fatal error: mysql_connection.h: No such file or directory解决方案:
# 确认头文件位置 sudo find / -name "mysql_connection.h" # 添加包含路径 g++ your_program.cpp -I/path/to/includes -lmysqlcppconn5.2 运行时问题
问题1:连接超时
CDK Error: Connection timeout解决方案:
// 增加连接超时设置 sql::ConnectOptionsMap options; options["hostName"] = "tcp://127.0.0.1:3306"; options["userName"] = "user"; options["password"] = "password"; options["OPT_CONNECT_TIMEOUT"] = 30; // 30秒超时 auto conn = driver->connect(options);问题2:字符集问题
Incorrect string value: '\xE4\xBD\xA0\xE5\xA5\xBD' for column 'name'解决方案:
// 连接时设置字符集 conn->setClientOption("characterSetResults", "utf8mb4"); conn->setClientOption("characterSetServer", "utf8mb4");5.3 性能问题
问题1:大数据量查询慢解决方案:
// 设置fetch size auto stmt = conn->createStatement(); stmt->setFetchSize(1000); // 每次获取1000条记录 auto res = stmt->executeQuery("SELECT * FROM large_table"); // 流式处理结果 while (res->next()) { // 处理单条记录 }问题2:频繁连接创建开销大解决方案:
- 使用连接池(如前文示例)
- 保持长连接并定期检查连接有效性
// 检查连接是否有效 if (!conn->isValid()) { conn->reconnect(); }6. 进阶技巧与最佳实践
6.1 元数据操作
Connector/C++提供了丰富的数据库元数据访问能力:
// 获取数据库元数据 auto meta = conn->getMetaData(); // 获取所有表 auto tables = std::unique_ptr<sql::ResultSet>( meta->getTables(conn->getSchema(), "", "%", {"TABLE"})); while (tables->next()) { std::cout << "表名: " << tables->getString("TABLE_NAME") << std::endl; } // 获取表结构 auto columns = std::unique_ptr<sql::ResultSet>( meta->getColumns(conn->getSchema(), "", "users", "%")); while (columns->next()) { std::cout << "列名: " << columns->getString("COLUMN_NAME") << ", 类型: " << columns->getString("TYPE_NAME") << ", 大小: " << columns->getInt("COLUMN_SIZE") << std::endl; }6.2 事务管理高级技巧
复杂业务逻辑需要精细的事务控制:
// 保存点示例 conn->setAutoCommit(false); try { // 操作1 stmt->executeUpdate("UPDATE accounts SET balance = balance - 100 WHERE id = 1"); // 设置保存点 auto savepoint = conn->setSavepoint("after_debit"); try { // 操作2 stmt->executeUpdate("UPDATE accounts SET balance = balance + 100 WHERE id = 2"); conn->commit(); } catch (...) { // 回滚到保存点 conn->rollback(savepoint); conn->releaseSavepoint(savepoint); throw; } } catch (...) { conn->rollback(); throw; }6.3 异步操作模式
虽然Connector/C++本身不支持异步IO,但可以通过线程池实现:
#include <future> #include <thread> class AsyncDBExecutor { public: AsyncDBExecutor(size_t threads) : pool_(threads) {} template<typename F, typename... Args> auto execute(F&& f, Args&&... args) { return pool_.enqueue(std::forward<F>(f), std::forward<Args>(args)...); } private: ThreadPool pool_; // 假设有一个线程池实现 }; // 使用示例 AsyncDBExecutor executor(4); auto future = executor.execute([](sql::Connection* conn) { auto stmt = conn->createStatement(); auto res = stmt->executeQuery("SELECT * FROM large_table"); // 处理结果... return processResults(res); }); // 其他工作... auto results = future.get(); // 获取异步结果6.4 自定义类型映射
处理复杂数据类型时,可以创建自定义类型转换器:
// JSON数据映射示例 class JsonData { public: JsonData(const std::string& json) : data_(parse(json)) {} std::string toDbString() const { return data_.dump(); } static JsonData fromDbString(const std::string& str) { return JsonData(str); } private: nlohmann::json data_; // 使用nlohmann/json库 }; // 使用自定义类型 pstmt->setString(1, jsonData.toDbString()); // 从结果集读取 auto json = JsonData::fromDbString(res->getString("json_field"));7. 测试与调试技巧
7.1 单元测试策略
为数据库相关代码编写有效的单元测试:
// 使用Google Test框架示例 class DBTest : public ::testing::Test { protected: void SetUp() override { driver = sql::mysql::get_mysql_driver_instance(); conn = std::unique_ptr<sql::Connection>( driver->connect("tcp://127.0.0.1:3306", "test", "test")); conn->setSchema("test_db"); // 初始化测试数据 auto stmt = conn->createStatement(); stmt->execute("CREATE TABLE IF NOT EXISTS users (id INT, name VARCHAR(100))"); stmt->execute("TRUNCATE TABLE users"); stmt->execute("INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob')"); } sql::mysql::MySQL_Driver* driver; std::unique_ptr<sql::Connection> conn; }; TEST_F(DBTest, UserQuery) { auto stmt = conn->createStatement(); auto res = stmt->executeQuery("SELECT name FROM users WHERE id = 1"); ASSERT_TRUE(res->next()); EXPECT_EQ(res->getString("name"), "Alice"); }7.2 性能分析与优化
使用工具分析数据库操作性能瓶颈:
// 简单的性能测量工具 class ScopedTimer { public: ScopedTimer(const std::string& name) : name_(name), start_(std::chrono::high_resolution_clock::now()) {} ~ScopedTimer() { auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start_); std::cout << name_ << " took " << duration.count() << "ms" << std::endl; } private: std::string name_; std::chrono::time_point<std::chrono::high_resolution_clock> start_; }; // 使用示例 { ScopedTimer timer("Database Query"); auto res = stmt->executeQuery("SELECT * FROM large_table"); while (res->next()) { // 处理结果 } }7.3 日志与错误追踪
实现详细的数据库操作日志:
class LoggingConnection : public sql::Connection { public: LoggingConnection(std::unique_ptr<sql::Connection> conn, std::ostream& log) : conn_(std::move(conn)), log_(log) {} std::unique_ptr<sql::Statement> createStatement() override { log_ << "Creating statement" << std::endl; return std::make_unique<LoggingStatement>(conn_->createStatement(), log_); } // 实现其他接口方法... private: std::unique_ptr<sql::Connection> conn_; std::ostream& log_; }; // 使用示例 std::ofstream db_log("database.log"); auto raw_conn = driver->connect("tcp://127.0.0.1:3306", "user", "password"); auto conn = std::make_unique<LoggingConnection>(std::move(raw_conn), db_log);8. 现代C++特性与Connector/C++
8.1 智能指针集成
Connector/C++原生接口返回的是原始指针,但可以轻松包装成智能指针:
template<typename T, typename Deleter> using connector_ptr = std::unique_ptr<T, Deleter>; struct StatementDeleter { void operator()(sql::Statement* stmt) const { delete stmt; } }; using StatementPtr = connector_ptr<sql::Statement, StatementDeleter>; struct ResultSetDeleter { void operator()(sql::ResultSet* res) const { delete res; } }; using ResultSetPtr = connector_ptr<sql::ResultSet, ResultSetDeleter>; // 使用示例 auto makeStatement(sql::Connection* conn) { return StatementPtr(conn->createStatement()); } auto stmt = makeStatement(conn.get()); auto res = ResultSetPtr(stmt->executeQuery("SELECT * FROM table"));8.2 Lambda表达式简化代码
利用lambda表达式处理结果集:
void processQueryResults(sql::Connection* conn, const std::string& query, std::function<void(const sql::ResultSet&)> processor) { auto stmt = conn->createStatement(); auto res = std::unique_ptr<sql::ResultSet>(stmt->executeQuery(query)); while (res->next()) { processor(*res); } } // 使用示例 processQueryResults(conn.get(), "SELECT * FROM users", [](const auto& row) { std::cout << "User: " << row.getString("name") << ", Age: " << row.getInt("age") << std::endl; });8.3 使用optional处理可能为空的值
std::optional<std::string> getUserEmail(sql::Connection* conn, int userId) { auto stmt = conn->createStatement(); auto res = std::unique_ptr<sql::ResultSet>( stmt->executeQuery("SELECT email FROM users WHERE id = " + std::to_string(userId))); if (res->next()) { if (res->isNull("email")) { return std::nullopt; } return res->getString("email"); } return std::nullopt; } // 使用示例 if (auto email = getUserEmail(conn.get(), 123)) { std::cout << "User email: " << *email << std::endl; } else { std::cout << "No email found or user doesn't exist" << std::endl; }9. 跨平台开发注意事项
9.1 Linux与Windows差异处理
连接字符串差异:
std::string getConnectionString() { #ifdef _WIN32 return "tcp://127.0.0.1:3306"; #else return "unix:///var/run/mysqld/mysqld.sock"; #endif }路径处理:
#include <filesystem> std::string getConfigPath() { namespace fs = std::filesystem; fs::path configDir; #ifdef _WIN32 configDir = getenv("APPDATA"); #else configDir = getenv("HOME"); configDir /= ".config"; #endif configDir /= "myapp"; fs::create_directories(configDir); return (configDir / "db.conf").string(); }9.2 不同编译器兼容性
预处理指令处理:
#if defined(__GNUC__) || defined(__clang__) #define LIKELY(x) __builtin_expect(!!(x), 1) #define UNLIKELY(x) __builtin_expect(!!(x), 0) #else #define LIKELY(x) (x) #define UNLIKELY(x) (x) #endif // 使用示例 if (UNLIKELY(conn->isClosed())) { throw std::runtime_error("Connection closed unexpectedly"); }异常处理:
try { // 数据库操作 } catch (const sql::SQLException& e) { std::cerr << "SQL Error: " << e.what() << " (MySQL error code: " << e.getErrorCode() << ", SQLState: " << e.getSQLState() << ")" << std::endl; } catch (const std::exception& e) { std::cerr << "Standard exception: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception occurred" << std::endl; }10. 安全最佳实践
10.1 凭证管理
永远不要将数据库凭证硬编码在代码中:
#include <fstream> #include <sstream> struct DBCredentials { std::string host; std::string user; std::string password; std::string database; }; DBCredentials loadCredentials(const std::string& path) { std::ifstream file(path); if (!file) { throw std::runtime_error("Cannot open credentials file"); } DBCredentials creds; std::string line; while (std::getline(file, line)) { auto pos = line.find('='); if (pos == std::string::npos) continue; std::string key = line.substr(0, pos); std::string value = line.substr(pos + 1); if (key == "host") creds.host = value; else if (key == "user") creds.user = value; else if (key == "password") creds.password = value; else if (key == "database") creds.database = value; } return creds; } // 使用示例 auto creds = loadCredentials("/etc/myapp/db.conf"); auto conn = driver->connect(creds.host, creds.user, creds.password); conn->setSchema(creds.database);10.2 SQL注入防护
始终使用预处理语句处理用户输入:
// 不安全的做法 std::string query = "SELECT * FROM users WHERE name = '" + userInput + "'"; auto res = stmt->executeQuery(query); // 安全的做法 auto pstmt = conn->prepareStatement("SELECT * FROM users WHERE name = ?"); pstmt->setString(1, userInput); auto res = pstmt->executeQuery();10.3 连接安全配置
sql::ConnectOptionsMap options; options["hostName"] = "tcp://127.0.0.1:3306"; options["userName"] = "app_user"; options["password"] = "secure_password"; options["sslKey"] = "/path/to/client-key.pem"; options["sslCert"] = "/path/to/client-cert.pem"; options["sslCA"] = "/path/to/ca.pem"; options["sslVerify"] = true; auto conn = driver->connect(options);11. 性能调优实战
11.1 查询优化技巧
使用EXPLAIN分析查询:
auto stmt = conn->createStatement(); auto explain = std::unique_ptr<sql::ResultSet>( stmt->executeQuery("EXPLAIN SELECT * FROM orders WHERE customer_id = 100")); std::cout << "Query Execution Plan:" << std::endl; while (explain->next()) { std::cout << "ID: " << explain->getInt("id") << ", Type: " << explain->getString("select_type") << ", Table: " << explain->getString("table") << ", Possible Keys: " << explain->getString("possible_keys") << ", Key: " << explain->getString("key") << ", Rows: " << explain->getInt("rows") << std::endl; }添加适当索引:
// 检查索引是否存在 auto indexCheck = std::unique_ptr<sql::ResultSet>( stmt->executeQuery("SHOW INDEX FROM orders WHERE Key_name = 'idx_customer'")); if (!indexCheck->next()) { // 创建索引 stmt->execute("ALTER TABLE orders ADD INDEX idx_customer (customer_id)"); }11.2 批量操作优化
批量插入性能对比:
| 方法 | 10,000条记录耗时(ms) | 内存使用(MB) |
|---|---|---|
| 单条INSERT | 12,345 | 45 |
| 多值INSERT | 1,234 | 12 |
| LOAD DATA INFILE | 345 | 8 |
| 预处理语句+批量提交 | 567 | 10 |
多值INSERT示例:
std::string buildMultiInsert(const std::vector<User>& users) { std::ostringstream oss; oss << "INSERT INTO users (name, email) VALUES "; for (size_t i = 0; i < users.size(); ++i) { if (i != 0) oss << ", "; oss << "('" << escapeSql(users[i].name) << "', " << "'" << escapeSql(users[i].email) << "')"; } return oss.str(); } // 使用示例 auto query = buildMultiInsert(users); stmt->executeUpdate(query);注意:虽然多值INSERT高效,但要小心SQL语句长度限制(默认约1MB)
11.3 连接池调优
连接池配置参数建议:
| 参数 | 建议值 | 说明 |
|---|---|---|
| 初始连接数 | CPU核心数 | 避免启动时连接风暴 |
| 最大连接数 | 初始连接数×3 | 应对突发负载 |
| 空闲超时 | 300秒 | 释放长时间未使用的连接 |
| 验证查询 | SELECT 1 | 定期检查连接健康状态 |
| 获取连接超时 | 5秒 | 避免长时间等待 |
动态调整连接池大小:
class AdaptiveConnectionPool : public ConnectionPool { public: void adjustSizeBasedOnLoad() { auto now = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastAdjust_); if (elapsed.count() < 60) return; // 每分钟最多调整一次 double avgWaitTime = totalWaitTime_ / waitCount_; if (avgWaitTime > 1.0 && currentSize_ < maxSize_) { // 增加连接数 addConnections(1); } else if (avgWaitTime < 0.1 && currentSize_ > minSize_) { // 减少连接数 removeConnection(); } lastAdjust_ = now; totalWaitTime_ = 0; waitCount_ = 0; } private: std::chrono::steady_clock::time_point lastAdjust_; double totalWaitTime_ = 0; int waitCount_ = 0; };12. 监控与维护
12.1 健康检查实现
class DatabaseHealthChecker { public: DatabaseHealthChecker(std::shared_ptr<ConnectionPool> pool, int intervalSec) : pool_(pool), interval_(intervalSec), running_(false) {} void start() { running_ = true; monitorThread_ = std::thread([this]() { while (running_) { checkDatabaseHealth(); std::this_thread::sleep_for(std::chrono::seconds(interval_)); } }); } void stop() { running_ = false; if (monitorThread_.joinable()) { monitorThread_.join(); } } bool isHealthy() const { std::lock_guard<std::mutex> lock(mutex_); return healthy_; } private: void checkDatabaseHealth() { try { auto conn = pool_->getConnection(); auto stmt = conn->createStatement(); auto res = stmt->executeQuery("SELECT 1"); res->next(); pool_->returnConnection(conn); std::lock_guard<std::mutex> lock(mutex_); healthy_ = true; } catch (...) { std::lock_guard<std::mutex> lock(mutex_); healthy_ = false; } } std::shared_ptr<ConnectionPool> pool_; int interval_; std::atomic<bool> running_; std::thread monitorThread_; mutable std::mutex mutex_; bool healthy_ = false; };12.2 性能指标收集
class DatabaseMetrics { public: struct QueryStats { std::string query; int64_t totalTimeMs = 0; int64_t count = 0; int64_t maxTimeMs = 0; int64_t minTimeMs = std::numeric_limits<int64_t>::max(); }; void recordQuery(const std::string& query, int64_t durationMs) { std::