545
社区成员
发帖
与我相关
我的任务
分享数据库连接池是一种维护若干数据库连接的技术,应用程序需要操作数据库时,从池中获取一个空闲连接,使用完毕后归还给池,而不是关闭连接。连接池通常负责连接的创建、分配、管理和释放。
基本工作流程如下:
同步连接是指发起数据库查询后,当前线程会阻塞直到查询完成并返回结果。代码通常这样写:
ResultSet* rs = conn->query("SELECT ...");
// 阻塞直到结果返回
processResult(rs);
异步连接发起查询后立即返回,不等待结果;当结果准备就绪时,通过回调函数、Future/Promise 或事件通知的方式通知调用方。
conn->asyncQuery("SELECT ...", [](ResultSet* rs) {
processResult(rs);
});
// 立即返回,线程可以继续处理其他任务
同步连接池通常与多线程配合:每个请求分配一个线程,从池中获取同步连接,执行查询,阻塞等待,然后返回。这种方式虽然复用了连接,但线程仍然阻塞在 I/O 上,通常使用在服务端启动时,用于初始化资源。

异步连接池则利用异步驱动(如 MySQL 的异步接口)或工作线程池:应用线程提交查询任务后立即返回,任务被放入队列,由专门的工作线程(通常较少)执行实际的同步查询,然后通过回调或 Future 将结果传回。这样应用线程不再阻塞,实现了真正的异步非阻塞。

我们实现的正是这种异步连接池。
一个连接池正是通过复用步骤1-2建立的长连接,避免重复握手和认证开销。

实现连接池需要与 MySQL 交互的底层库。常用的有:
我们的目标是实现一个高效的异步 MySQL 连接池,核心思想是:
Query 方法提交 SQL 语句和一个回调函数。SQLOperation,放入阻塞队列。ResultSet。std::promise 和 std::future 将结果传递给调用方,触发回调。下面逐一介绍各个类的作用与实现要点。
阻塞队列是任务传递的核心。它内部使用 std::queue 存储任务,利用互斥锁和条件变量实现线程安全的入队和出队。当队列为空时,Pop 操作会阻塞直到有任务加入或队列被设置为非阻塞模式。
关键设计:
non_block_ 标志支持优雅关闭:当连接池销毁时,设置该标志并唤醒所有等待线程,让它们退出。Push 操作通知一个等待线程,避免“惊群效应”。每个 SQLOperation 封装一条 SQL 语句和一个 std::promise<std::unique_ptr<sql::ResultSet>>。工作线程执行 SQL 后,通过 promise_.set_value() 将结果传递给 Future。
MySQLConn 代表一个物理的数据库连接,它包含:
sql::Driver 和 sql::Connection 对象。MySQLWorker 工作线程。Open() 方法建立真实连接,Query() 方法执行同步查询(由工作线程调用)。
每个 MySQLConn 拥有一个专属的 MySQLWorker,后者在独立线程中运行 Worker() 函数。该函数不断从阻塞队列中取出 SQLOperation,调用 conn_->Query(op->sql_) 执行,然后将结果设置到 op 的 Promise 中。
这种设计将 I/O 操作(SQL 执行)放在工作线程中,应用线程可以继续处理其他逻辑,实现了异步。
QueryCallback 用于管理回调的调用时机。它持有一个 std::future 和用户回调函数。InvokeIfReady() 方法检查 Future 是否已就绪(即结果已返回),若就绪则获取结果并调用回调。
这个类可以用于事件循环中定期检查哪些查询已完成,从而调用回调。在简单的示例中,我们可能直接在主线程中等待 Future,但这里的设计更符合异步风格:将 Future 与回调绑定,由连接池或外部循环统一触发。
连接池是单例(按数据库名区分),负责初始化一组连接,提供统一的查询接口 Query()。
InitPool() 创建指定数量的 MySQLConn,每个连接启动其工作线程。Query() 接受 SQL 和回调,创建一个 SQLOperation,将其 future 与回调封装为 QueryCallback 返回给调用方(或者放入某个待处理列表)。同时将操作 Push 到阻塞队列中。说明:
instances_ 静态成员管理不同数据库的单例实例。Query 返回 QueryCallback 对象,便于上层在合适的时机调用回调。#pragma once
#include <vector>
#include <string>
#include <functional>
#include <unordered_map>
#include <memory>
#include <cppconn/resultset.h>
#include "QueryCallback.h"
// 前置声明
template <typename T>
class BlockingQueue;
class MySQLConn; // 连接
class SQLOperation; // 操作
class MySQLConnPool {
public:
static MySQLConnPool *GetInstance(const std::string &db); // 获取单例
void InitPool(const std::string &url, size_t pool_size); // 初始化连接池
QueryCallback Query(const std::string &sql, std::function<void(std::unique_ptr<sql::ResultSet>)> &&cb);
private:
MySQLConnPool(const std::string &db) : database_(db) {}
~MySQLConnPool();
std::string database_;
std::vector<MySQLConn *> pool_;
static std::unordered_map<std::string, MySQLConnPool *> instances_; // 声明
BlockingQueue<SQLOperation *> *task_queue_;
};
说明:
MySQLConnInfo 负责解析连接字符串(格式如 tcp://127.0.0.1:3306;user=root;password=123)。Open() 中初始化 driver_ 并建立连接。Query() 由工作线程调用,执行同步查询并返回原始指针(所有权转移给调用者,通常包装为 unique_ptr)。#pragma once
#include <cppconn/driver.h>
#include <cppconn/connection.h>
#include <string>
// 前置声明
namespace sql {
class Driver;
class Connection;
class SQLException;
class Statement;
class ResultSet;
}
template <typename T>
class BlockingQueue;
class SQLOperation; // 操作
class MySQLWorker; // 工作线程
struct MySQLConnInfo {
explicit MySQLConnInfo(const std::string &info, const std::string &db);
std::string user;
std::string password;
std::string databse;
std::string url;
};
class MySQLConn {
public:
MySQLConn(const std::string &info, const std::string &db, BlockingQueue<SQLOperation*> &task_queue);
~MySQLConn();
int Open(); // 建立与MySQL数据库的连接
void Close();
sql::ResultSet *Query(const std::string &sql);
private:
void HandlerException(sql::SQLException &e);
sql::Driver *driver_;
sql::Connection *conn_;
sql::Statement *stmt_;
MySQLWorker *worker_;
MySQLConnInfo info_;
};
说明:
Start() 创建线程并运行 Worker。Worker 循环调用 task_queue_.Pop(op),若取到任务则执行 op->Execute(conn_),然后 delete op。Stop() 设置队列为非阻塞模式并等待线程结束。#pragma once
#include <thread>
// 前置声明
template <typename T>
class BlockingQueue;
class MySQLConn; // 连接
class SQLOperation; // 操作
class MySQLWorker {
public:
MySQLWorker(MySQLConn *conn, BlockingQueue<SQLOperation*> &task_queue);
~MySQLWorker();
void Start();
void Stop();
private:
void Worker(); // 线程入口函数
MySQLConn *conn_; // 连接
std::thread worker_;
BlockingQueue<SQLOperation*> &task_queue_; // 队列
};
说明:
InvokeIfReady 是非阻塞检查,可用于事件循环中定期触发回调。sql::ResultSet 必须为完整类型,因为 unique_ptr 需要知道删除器。#pragma once
#include <chrono>
#include <functional>
#include <future>
#include <memory>
#include <cppconn/resultset.h>
class QueryCallback {
public:
QueryCallback(std::future<std::unique_ptr<sql::ResultSet>> &&future,
std::function<void(std::unique_ptr<sql::ResultSet>)> &&cb)
: future_(std::move(future)), cb_(std::move(cb)) {}
bool InvokeIfReady() {
if (future_.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
auto result = future_.get();
cb_(std::move(result));
return true;
}
return false;
}
private:
std::future<std::unique_ptr<sql::ResultSet>> future_;
std::function<void(std::unique_ptr<sql::ResultSet>)> cb_;
};
说明:
Execute 中执行查询,并将结果或异常设置到 promise。std::unique_ptr 自动管理 ResultSet 生命周期。#pragma once
#include <string>
#include <future>
#include <memory>
// NOTE: sql::ResultSet 必须完整才能进行 std::unique_ptr 的默认删除。
#include <cppconn/resultset.h>
class MySQLConn; // 连接
class SQLOperation {
public:
explicit SQLOperation(const std::string &sql) : sql_(sql) {}
void Execute(MySQLConn *conn); // 用于执行SQL操作并设置结果
std::future<std::unique_ptr<sql::ResultSet>> GetFuture() {
return promise_.get_future();
}
private:
std::string sql_;
std::promise<std::unique_ptr<sql::ResultSet>> promise_;
};
说明:
Pop 在队列为空且非阻塞模式未开启时阻塞。Cancel 用于通知所有等待线程退出。#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
template <typename T>
class BlockingQueue {
public:
explicit BlockingQueue(bool non_block = false) : non_block_(non_block) {}
void Push(const T &value) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(value);
not_empty_.notify_one();
}
bool Pop(T &value) {
std::unique_lock<std::mutex> lock(mutex_);
not_empty_.wait(lock, [this] { return !queue_.empty() || non_block_;});
if(queue_.empty()) return false;
value = queue_.front();
queue_.pop();
return true;
}
void Cancel() {
std::lock_guard<std::mutex> lock(mutex_);
non_block_ = true;
not_empty_.notify_all();
}
private:
bool non_block_; // 是否为非阻塞模式 默认为阻塞模式
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable not_empty_; // 非空条件变量
};
#include "MySQLConnPool.h"
#include "AsyncProcessor.h"
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <cppconn/resultset.h>
// g++ -o main main.cpp AsyncProcessor.cpp MySQLConn.cpp MySQLConnPool.cpp MySQLWorker.cpp SQLOperation.cpp -lpthread -std=c++17 -lmysqlcppconn
void HandleQueryResult(std::unique_ptr<sql::ResultSet> result) {
while (result->next()) {
std::cout << "cid: " << result->getInt("cid") << ", caption: " << result->getString("caption") << std::endl;
}
}
int main() {
// 创建连接池
MySQLConnPool* pool = MySQLConnPool::GetInstance("edu_svc");
pool->InitPool("tcp://127.0.0.1:3306;xiaonie;0524", 10);
// 创建异步处理器
AsyncProcessor response_handler;
// 创建查询任务
auto query_callback = pool->Query("SELECT * FROM class;", HandleQueryResult);
response_handler.AddQueryCallback(std::move(query_callback)); // 将查询任务添加到异步处理器中
// 等待异步处理完成
while (true) {
response_handler.InvokeIfReady();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
本文从数据库连接池的概念入手,分析了同步/异步连接的区别,并详细实现了一个基于 C++ 的异步 MySQL 连接池。该连接池利用多线程 + 阻塞队列 + Promise/Future 实现了高效的异步查询,让应用线程从 I/O 等待中解放出来,提高了并发处理能力。
https://blog.csdn.net/qq_57951250/article/details/159043593?spm=1011.2124.3001.6209