C++实战:使用原生API构建MySQL数据库连接与操作模块

📅 2026/7/15 21:52:19 👁️ 阅读次数 📝 编程学习
C++实战:使用原生API构建MySQL数据库连接与操作模块

1. 环境准备与基础配置

在开始使用C++原生API操作MySQL之前,我们需要先搭建开发环境。这里以Windows平台为例(Linux/macOS配置原理类似),假设你已经安装好了MySQL Server和Visual Studio开发环境。

关键步骤:

  1. 找到MySQL安装目录下的includelib文件夹(通常位于C:\Program Files\MySQL\MySQL Server 8.0
  2. 在VS项目中配置附加包含目录:右键项目 → 属性 → C/C++ → 常规 → 附加包含目录
  3. 配置库目录:链接器 → 常规 → 附加库目录
  4. 添加依赖项:链接器 → 输入 → 附加依赖项中添加libmysql.lib

注意:运行时需要将libmysql.dll放在可执行文件同级目录或系统PATH路径下。我第一次配置时曾因为漏掉这一步,程序运行时直接崩溃,调试了半天才发现问题。

2. 核心API封装设计

2.1 连接管理类封装

我们先设计一个基础的MySQL连接类,采用RAII机制管理资源:

class MySQLConnection { public: MySQLConnection(const std::string& host, const std::string& user, const std::string& password, const std::string& db, unsigned int port = 3306) : m_conn(nullptr) { mysql_init(&m_mysql); m_conn = mysql_real_connect(&m_mysql, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0); if (!m_conn) { throw std::runtime_error(mysql_error(&m_mysql)); } } ~MySQLConnection() { if (m_conn) { mysql_close(&m_mysql); } } // 禁用拷贝构造和赋值 MySQLConnection(const MySQLConnection&) = delete; MySQLConnection& operator=(const MySQLConnection&) = delete; private: MYSQL m_mysql; MYSQL* m_conn; };

这个基础版本已经实现了:

  • 自动初始化连接
  • 异常安全的资源释放
  • 防止意外的对象拷贝

2.2 查询执行与结果处理

接下来我们扩展查询功能,添加一个执行SQL的方法:

class MySQLQuery { public: explicit MySQLQuery(MySQLConnection& conn) : m_conn(conn), m_res(nullptr) {} bool execute(const std::string& sql) { if (mysql_real_query(m_conn.get(), sql.c_str(), sql.length()) != 0) { m_error = mysql_error(m_conn.get()); return false; } m_res = mysql_store_result(m_conn.get()); return true; } std::vector<std::vector<std::string>> fetchAll() { std::vector<std::vector<std::string>> results; if (!m_res) return results; int num_fields = mysql_num_fields(m_res); MYSQL_ROW row; while ((row = mysql_fetch_row(m_res))) { std::vector<std::string> record; for (int i = 0; i < num_fields; ++i) { record.emplace_back(row[i] ? row[i] : "NULL"); } results.emplace_back(std::move(record)); } return results; } ~MySQLQuery() { if (m_res) { mysql_free_result(m_res); } } private: MySQLConnection& m_conn; MYSQL_RES* m_res; std::string m_error; };

3. 高级功能实现

3.1 参数化查询防注入

直接拼接SQL语句存在SQL注入风险。我们可以实现一个简单的参数化查询:

class MySQLStatement { public: explicit MySQLStatement(MySQLConnection& conn) : m_conn(conn), m_stmt(nullptr) { m_stmt = mysql_stmt_init(m_conn.get()); if (!m_stmt) { throw std::runtime_error(mysql_error(m_conn.get())); } } bool prepare(const std::string& sql) { if (mysql_stmt_prepare(m_stmt, sql.c_str(), sql.length()) != 0) { m_error = mysql_stmt_error(m_stmt); return false; } return true; } bool execute() { if (mysql_stmt_execute(m_stmt) != 0) { m_error = mysql_stmt_error(m_stmt); return false; } return true; } ~MySQLStatement() { if (m_stmt) { mysql_stmt_close(m_stmt); } } private: MySQLConnection& m_conn; MYSQL_STMT* m_stmt; std::string m_error; };

使用示例:

MySQLConnection conn("localhost", "user", "password", "test_db"); MySQLStatement stmt(conn); stmt.prepare("INSERT INTO users (name, age) VALUES (?, ?)"); // 实际使用时需要绑定参数 // MYSQL_BIND bind[2]; // ...设置bind参数... // mysql_stmt_bind_param(m_stmt, bind);

3.2 连接池实现

对于高并发场景,频繁创建销毁连接会影响性能。下面是一个简单的连接池实现思路:

class MySQLConnectionPool { public: MySQLConnectionPool(size_t pool_size, const std::string& host, ...) : m_host(host), ... { for (size_t i = 0; i < pool_size; ++i) { m_pool.emplace(std::make_shared<MySQLConnection>(host, ...)); } } std::shared_ptr<MySQLConnection> getConnection() { std::unique_lock<std::mutex> lock(m_mutex); m_cond.wait(lock, [this] { return !m_pool.empty(); }); auto conn = m_pool.front(); m_pool.pop(); return conn; } void returnConnection(std::shared_ptr<MySQLConnection> conn) { std::unique_lock<std::mutex> lock(m_mutex); m_pool.push(conn); m_cond.notify_one(); } private: std::queue<std::shared_ptr<MySQLConnection>> m_pool; std::mutex m_mutex; std::condition_variable m_cond; std::string m_host; // 其他连接参数... };

4. 错误处理与调试技巧

4.1 完善的错误处理机制

MySQL API的错误处理有几个关键点需要注意:

  1. 每次API调用后都应检查返回值
  2. 错误信息需要通过mysql_error()mysql_stmt_error()获取
  3. 错误码可以通过mysql_errno()获取

建议封装一个统一的错误处理类:

class MySQLError { public: static void check(MYSQL* conn, int ret) { if (ret != 0) { throw std::runtime_error( std::to_string(mysql_errno(conn)) + ": " + mysql_error(conn)); } } static void check_stmt(MYSQL_STMT* stmt, int ret) { if (ret != 0) { throw std::runtime_error( std::to_string(mysql_stmt_errno(stmt)) + ": " + mysql_stmt_error(stmt)); } } };

4.2 常见问题排查

  1. 连接失败:检查MySQL服务是否运行、用户名密码是否正确、是否有远程访问权限
  2. 查询返回乱码:确保连接后设置了正确的字符集mysql_set_character_set(&mysql, "utf8mb4")
  3. 内存泄漏:确保每个mysql_store_result()都有对应的mysql_free_result()
  4. 连接超时:MySQL默认有8小时闲置断开机制,可以在连接字符串中添加CLIENT_RECONNECT标志

5. 性能优化建议

  1. 批量操作:对于大量插入,使用INSERT INTO ... VALUES (...), (...), ...比多次单条插入快10倍以上
  2. 预处理语句复用:创建一次预处理语句,多次执行比每次都准备新语句高效
  3. 合理使用事务:将多个写操作放在一个事务中,减少磁盘I/O
  4. 结果集处理:对于大结果集,使用mysql_use_result()替代mysql_store_result()减少内存占用
  5. 连接池大小:一般设置为CPU核心数的2-3倍

我曾经在一个日志处理项目中,通过将单条插入改为批量插入+事务,使性能从每秒200条提升到15000条。