基础组件(三):mysql连接池

发布于:2025-08-03 ⋅ 阅读:(14) ⋅ 点赞:(0)


一、MySQL连接池设计

数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。

1. 连接池解决了什么问题?

连接池的作用 (好处)

  • 资源复用
    减少资源浪费,不再频繁创建和销毁连接,降低了系统资源的消耗。if不使用连接池,每次数据库请求都新建一条连接,将耗费系统资源。
  • 更快的系统响应速度
    一次连接的建立和销毁,可复用同一条连接多次执行SQL语句。
    通过复用连接池中的连接,避免了重复的连接创建过程,大大缩短了数据库操作的响应时间,在高并发场景下,显著提升了系统的整体性能。
  • 提升应用稳定性
    连接池可以对连接进行统一管理,当出现连接泄漏(即应用程序获取连接后未正确归还)等问题时,连接池能够进行一定程度的检测和处理,避免因连接问题导致应用程序崩溃。

为什么不创建多条连接而用连接池

因为同一条数据库连接上串行执行sql语句的,而并发执行(创建多条连接)sql语句带来的副作用是需要考虑事务。本质连接池是一个数据库连接的缓存容器


2. 同步和异步连接池的区别

同步连接池(场景局限,应用服务器启动时初始化资源)

请添加图片描述
当前线程从连接池(线程安全)中获取可用连接( 未被锁定的连接),描述当前最多允许几个线程或协程并发使用连接。

异步连接池(应用在服务器启动后处理业务)

请添加图片描述
任意线程向连接池投递SQL语句执行请求,连接池依次从队列里取任务执行。用户请求间接(通过异步回调接收数据库返回)获取数据库应
答。描述着当前最多允许几个连接同时执行 SQL 语句。

  • 获取返回值
    同步链接(Synchronous): 通过 接口的返回值 接受数据库返回值,主线程会堵塞
    异步连接(Asynchronous): 通过 回调函数 接受数据库返回值,主线程不会堵塞

3. 单条MySQL连接过程

请添加图片描述
第一次访问的时候需要建立连接,但是之后的访问,均会复用之前的创建的连接,直接执行SQL语句即可.

每次执行一条SQL语句的网络交互有:
1)TCP建立连接的三次握手(客户端与mysql服务器的连接基于tcp)
2) MySQL认证的三次握手
3) 真正的SQL执行
4) MySQL的关闭
5) TCP的四次挥手关闭

连接池的运行流程
它在系统初始化时创建一定数量的数据库连接对象,并将这些连接维护在一个池中。当应用程序需要与 MySQL 数据库进行交互时,无需重新创建新的连接,而是从连接池中获取一个已有的连接;
当操作完成后,再将该连接归还到连接池中,以便后续其他请求复用。这种方式大大减少了连接创建和销毁的开销。

  • MySQL连接的要素:主机IP、主机端口、用户名、密码
    连接池连接数公式: 连接数=(核心数*2)+有效磁盘数
    连接池与线程池区别:连接池被动使用,线程池是主动不断的从队列中去执行任务

4. 连接池的实现

优秀笔记: MySQL连接池使用步骤

安装接口库

//MYSQL的驱动
libmysqlclient     //纯c实现
libmysqlcppconn    //c++实现,使用了异常机制
//阻塞io

=================================

//安装 libmysqlcppconn
sudo apt-get install libmysqlcppconn-dev
 
**头文件: /usr/include/cppconn/*.h
**库文件: /uer/lib/x86_64-linux-gnu/
    libmysqlcppconn.a
    libmysqlcppconn.so

异步连接

基于连接去执行命令(send)和等待结果(recv)的过程是一个耗时操作,考虑使用线程池去实现。

因为线程要获取,命令的返回值结果作为执行命令的参数,所以我们引入future-promise,来获取结果

主线程创建 promise 并获取 future。
新线程执行 database_query(),查询完成后调用 prom.set_value(result) 传递查询结果。
主线程阻塞等待 fut.get() 获取 SQL 查询结果

代码实现

//MySQLConnPool.h
 
namespace sql {
    class ResultSet;
}
//前置声明  防止依赖过深
class MySQLConn;
template <typename T>
class BlockingQueue;
 
class SQLOperation;
 
 
class MySQLConnPool {
public:
    //获取单例
    static MySQLConnPool *GetInstance(const std::string &db);
 
    void InitPool(const std::string &url, int pool_size);
    //输入sql语句,执行对应的回调函数
    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_;
    //因为数据库有多个,但是我们每次只访问一个,所以用单例模式
    //一个数据库对应一个单例,所以用unordered_map存储
    static std::unordered_map<std::string, MySQLConnPool *> instances_;
    //阻塞队列 存储sql操作
    BlockingQueue<SQLOperation *> *task_queue_;
};
 
 
//MySQLConn.h
 
//前置声明
namespace sql 
{
    class Driver;
    class Connection;
    class SQLException;
    class ResultSet;
}
 
class MySQLWorker;
 
template <typename T>
class BlockingQueue;
 
class SQLOperation;
 
struct MySQLConnInfo {
    explicit MySQLConnInfo(const std::string &info, const std::string &db);
    std::string user;
    std::string password;
    std::string database;
    std::string url;
};
 
class MySQLConn {
public:
    MySQLConn(const std::string &info, const std::string &db, BlockingQueue<SQLOperation *> &task_queue);
    ~MySQLConn();
 
    //自定义 连接函数,防止连接中断
    int Open();
    void Close();
 
    //执行sql语句
    sql::ResultSet* Query(const std::string &sql);
 
    
private:
    //异常处理函数
    void HandlerException(sql::SQLException &e);
    //sql驱动,连接
    sql::Driver *driver_;
    sql::Connection *conn_;
    //对应的线程对象
    MySQLWorker *worker_;
    
    MySQLConnInfo info_;
};
 
 
//MySQLWorker.h
 
class MySQLConn;
 
template <typename T>
class BlockingQueue;
 
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_;
};
 
//SQLOperation.h
 
namespace sql
{
    class ResultSet;
}
 
class MySQLConn;
 
class SQLOperation {
public:
    explicit SQLOperation(const std::string &sql) : sql_(sql) {}
    void Execute(MySQLConn *conn);
 
    std::future<std::unique_ptr<sql::ResultSet>> GetFuture() {
        return promise_.get_future();
    }
 
private:
    std::string sql_;
    std::promise<std::unique_ptr<sql::ResultSet>> promise_;
};
 
//QueryCallback.h
 
namespace sql
{
    class ResultSet;
}
 
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) {
            cb_(std::move(future_.get()));
            return true;
        }
        return false;
    }
private:
    std::future<std::unique_ptr<sql::ResultSet>> future_;
    std::function<void(std::unique_ptr<sql::ResultSet>)> cb_;
};
 
//AsyncProcessor.h
 
class QueryCallback;
class AsyncProcessor
{
public:
    void AddQueryCallback(QueryCallback &&query_callback);
    void InvokeIfReady();
 
private:
    std::vector<QueryCallback> pending_queries_;
};

优秀笔记:
1. 池式结构–MYSQL连接池
2. mysql连接池的实现
3. MySQL连接池原理及设计
参考学习:https://github.com/0voice


网站公告

今日签到

点亮在社区的每一天
去签到