文章目录
- 一、MySQL连接池设计
- 1. 连接池解决了什么问题?
- 连接池的作用 (好处)
- 为什么不创建多条连接而用连接池
- 2. 同步和异步连接池的区别
- 同步连接池(场景局限,应用服务器启动时初始化资源)
- 异步连接池(应用在服务器启动后处理业务)
- 3. 单条MySQL连接过程
- 4. 连接池的实现
- 安装接口库
- 异步连接
- 代码实现
一、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.alibmysqlcppconn.so
异步连接
基于连接去执行命令(send)和等待结果(recv)的过程是一个耗时操作,考虑使用线程池去实现。
因为线程要获取,命令的返回值结果作为执行命令的参数,所以我们引入future-promise,来获取结果
主线程创建 promise 并获取 future。
新线程执行 database_query(),查询完成后调用 prom.set_value(result) 传递查询结果。
主线程阻塞等待 fut.get() 获取 SQL 查询结果
代码实现
//MySQLConnPool.hnamespace 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.hclass 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.hnamespace 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.hnamespace 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.hclass 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