文章目錄
- 一、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