從零開始實現 C++ TinyWebServer 數據庫連接池 SqlConnectPool詳解

文章目錄

  • 數據庫連接池是什么?
  • Web Server 中為什么需要數據庫連接池?
  • SqlConnectPool 成員變量
  • 實現 Init() 函數
  • 實現 ClosePool() 函數
  • SqlConnectRAII 類
  • SqlConnectPool 代碼
  • SqlConnectPool 測試

從零開始實現 C++ TinyWebServer 項目總覽
項目源碼

數據庫連接池是什么?

前面實現了線程池,而數據庫連接池的思想與線程池類似,都是將數據庫連接或線程并存儲在一個 “池”(容器)中,便于后續使用。

數據庫連接池是一種數據庫連接管理技術,它預先創建一定數量的數據庫連接并存儲在一個 “池” 中。當應用程序需要與數據庫進行交互時,它不是直接創建新的數據庫連接,而是從連接池中獲取一個已經建立好的連接;當應用程序使用完連接后,將連接歸還給連接池,而不是直接關閉連接。這樣,連接可以被重復使用,避免了頻繁創建和銷毀連接帶來的開銷。

數據庫連接池的作用

  1. 減少資源消耗:創建和銷毀數據庫連接是一個相對昂貴的操作,涉及到網絡通信、身份驗證、資源分配等多個步驟。使用連接池可以復用已經創建好的連接,減少了頻繁創建和銷毀連接所帶來的系統資源消耗,提高了系統的性能。
  2. 提高響應速度:由于連接池中的連接已經預先創建好,應用程序可以直接從連接池中獲取連接,無需等待新連接的創建過程,從而顯著提高了應用程序的響應速度。
  3. 統一管理連接:連接池可以對連接進行統一管理,可以有效地控制數據庫連接的使用,避免因連接過多導致數據庫服務器資源耗盡。
  4. 增強系統的穩定性:連接池可以監控連接的狀態,當連接出現異常時,可以及時進行處理,如重新建立連接等。這有助于增強系統的穩定性,減少因連接問題導致的系統故障。

Web Server 中為什么需要數據庫連接池?

在 Web Server 中,通常會面臨大量的并發請求,每個請求可能都需要與數據庫進行交互。如果沒有數據庫連接池,每次請求都要創建一個新的數據庫連接,會導致以下問題:

  1. 性能瓶頸:頻繁創建和銷毀數據庫連接會消耗大量的系統資源,導致 Web Server 的性能下降,響應時間變長。
  2. 資源耗盡:如果并發請求過多,不斷創建新的連接可能會耗盡數據庫服務器和 Web Server 的資源,導致系統崩潰。
  3. 管理困難:大量的連接會增加管理的難度,例如難以控制連接的數量和狀態,容易出現連接泄漏等問題。

SqlConnectPool 成員變量

int max_connect_;
std::queue<MYSQL*> connect_queue_;
std::mutex mtx_;
sem_t sem_id_;
  • connect_queue_:使用隊列存儲數據庫連接。
  • sem_id_:信號量,用于控制連接的獲取和釋放,確保連接的數量不超過最大連接數。

實現 Init() 函數

  • 遍歷指定的連接數量,使用 mysql_init 初始化 MySQL 連接對象,使用 mysql_real_connect 建立與數據庫的連接。
  • 如果連接成功,將連接對象存入連接隊列,并增加最大連接數。
  • 最后使用 sem_init 初始化信號量,信號量的初始值為最大連接數。
void SqlConnectPool::Init(const char* host, uint16_t port, const char* user, const char* pwd,const char* db_name, int connect_size) {assert(connect_size > 0);for (int i = 0; i < connect_size; ++i) {MYSQL* connect = nullptr;connect = mysql_init(nullptr);if (!connect) {LOG_ERROR("MySql init fail");assert(connect);}if (!mysql_real_connect(connect, host, user, pwd, db_name, port, nullptr, 0)) {LOG_ERROR("MySql failed to connect to database: Error: %s", mysql_error(connect));} else {connect_queue_.emplace(connect);max_connect_++;}}assert(max_connect_ > 0);// 線程級信號量sem_init(&sem_id_, 0, max_connect_);
}

實現 ClosePool() 函數

  • 使用 std::lock_guard 加鎖,確保多線程環境下對連接隊列的安全訪問。
  • 遍歷連接隊列,使用 mysql_close 關閉所有的數據庫連接,并調用 mysql_library_end 結束 MySQL 庫的使用。
void SqlConnectPool::ClosePool() {std::lock_guard<std::mutex> locker(mtx_);while (!connect_queue_.empty()) {MYSQL* connect = connect_queue_.front();connect_queue_.pop();mysql_close(connect);}mysql_library_end();
}

SqlConnectRAII 類

在構造函數中從連接池獲取一個數據庫連接,在析構函數中自動將連接放回連接池,確保連接的正確釋放,避免內存泄漏。

// 構造時初始化,析構時釋放
class SqlConnectRAII {
public:SqlConnectRAII(MYSQL** sql, SqlConnectPool* pool) {assert(pool);*sql = pool->GetConnect();sql_ = *sql;pool_ = pool;}~SqlConnectRAII() {if (sql_)pool_->FreeConnect(sql_);}private:MYSQL* sql_;SqlConnectPool* pool_;
};

SqlConnectPool 代碼

sql_connect_pool.h

#ifndef SQL_CONNECT_POOL_H
#define SQL_CONNECT_POOL_H#include <string>
#include <queue>
#include <mutex>
#include <thread>
#include <cassert>
#include <semaphore.h>
#include <mysql/mysql.h>
#include "../log/log.h"class SqlConnectPool {
public:static SqlConnectPool* GetInstance();void Init(const char* host, uint16_t port, const char* user, const char* pwd,const char* db_name, int connect_size = 10);void ClosePool();MYSQL* GetConnect();void FreeConnect(MYSQL* connect);int GetFreeConnectCount();private:SqlConnectPool() = default;~SqlConnectPool() { ClosePool(); }int max_connect_;std::queue<MYSQL*> connect_queue_;std::mutex mtx_;sem_t sem_id_;
};// 構造時初始化,析構時釋放
class SqlConnectRAII {
public:SqlConnectRAII(MYSQL** sql, SqlConnectPool* pool) {assert(pool);*sql = pool->GetConnect();sql_ = *sql;pool_ = pool;}~SqlConnectRAII() {if (sql_)pool_->FreeConnect(sql_);}private:MYSQL* sql_;SqlConnectPool* pool_;
};#endif // SQL_CONNECT_POOL_H

sql_connect_pool.cc

#include "sql_connect_pool.h"SqlConnectPool* SqlConnectPool::GetInstance() {static SqlConnectPool pool;return &pool;
}void SqlConnectPool::Init(const char* host, uint16_t port, const char* user, const char* pwd,const char* db_name, int connect_size) {assert(connect_size > 0);for (int i = 0; i < connect_size; ++i) {MYSQL* connect = nullptr;connect = mysql_init(nullptr);if (!connect) {LOG_ERROR("MySql init fail");assert(connect);}if (!mysql_real_connect(connect, host, user, pwd, db_name, port, nullptr, 0)) {LOG_ERROR("MySql failed to connect to database: Error: %s", mysql_error(connect));} else {connect_queue_.emplace(connect);max_connect_++;}}assert(max_connect_ > 0);// 線程級信號量sem_init(&sem_id_, 0, max_connect_);
}void SqlConnectPool::ClosePool() {std::lock_guard<std::mutex> locker(mtx_);while (!connect_queue_.empty()) {MYSQL* connect = connect_queue_.front();connect_queue_.pop();mysql_close(connect);}mysql_library_end();
}MYSQL* SqlConnectPool::GetConnect() {MYSQL* connect = nullptr;if (connect_queue_.empty()) {LOG_WARN("SqlConnectPool busy!");return nullptr;} sem_wait(&sem_id_); // -1std::lock_guard<std::mutex> locker(mtx_);connect = connect_queue_.front();connect_queue_.pop();return connect;
}// 存入連接池
void SqlConnectPool::FreeConnect(MYSQL* connect) {assert(connect);std::lock_guard<std::mutex> locker(mtx_);connect_queue_.push(connect);sem_post(&sem_id_); // +1
}int SqlConnectPool::GetFreeConnectCount() {std::lock_guard<std::mutex> locker(mtx_);return connect_queue_.size();
}

SqlConnectPool 測試

測試SqlConnectPool中MYSQL的初始化,連接,以及查詢語句,以及SqlConnRAII 類

#include "../code/pool/sql_connect_pool.h"
#include <iostream>// 測試 SqlConnectPool 類的功能
void TestSqlConnectPool() {// 初始化日志系統Log* logger = Log::GetInstance();logger->Init(0, "./logs/", ".log", 1024);// 獲取 SqlConnectPool 的單例實例SqlConnectPool* pool = SqlConnectPool::GetInstance();// 初始化連接池const char* host = "localhost";uint16_t port = 3306;const char* user = "Tian";const char* pwd = "123456";const char* db_name = "web_server";int connect_size = 10;pool->Init(host, port, user, pwd, db_name, connect_size);// 測試獲取連接MYSQL* conn = pool->GetConnect();assert(conn != nullptr);  // 確保獲取到的連接不為空// 執行簡單的 SQL 查詢if (mysql_query(conn, "select 1")) {std::cerr << "query failed: " << mysql_error(conn) << std::endl;} else {MYSQL_RES* result = mysql_store_result(conn);if (result) {MYSQL_ROW row = mysql_fetch_row(result);if (row) {std::cout << "Query result: " << row[0] << std::endl;}mysql_free_result(result);}}// 測試釋放連接pool->FreeConnect(conn);// 測試獲取空閑連接數量int freeCount = pool->GetFreeConnectCount();std::cout << "Free connection count: " << freeCount << std::endl;// 測試 SqlConnRAII 類{MYSQL* sql = nullptr;SqlConnectRAII raii(&sql, pool);assert(sql != nullptr);  // 確保通過 RAII 獲取到的連接不為空}  // 離開作用域,自動釋放連接// 關閉連接池pool->ClosePool();
}int main() {TestSqlConnectPool();return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(buffer_unit_test)# 設置 C++ 標準和編譯器選項
# set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")# 定義公共源文件和特定文件
set(COMMON_SOURCES ../code/buffer/buffer.cc ../code/log/log.cc)
set(POOL_SOURCE ../code/pool/sql_connect_pool.cc)# 查找 MySQL 庫
find_package(PkgConfig REQUIRED)
pkg_check_modules(MYSQL REQUIRED mysqlclient)
# 包含 MySQL 頭文件目錄
include_directories(${MYSQL_INCLUDE_DIR})# 添加可執行文件
add_executable(sql_connect_pool_test sql_connect_pool_test.cc ${COMMON_SOURCES} ${POOL_SOURCE})# 鏈接庫
target_link_libraries(sql_connect_pool_test ${MYSQL_LIBRARIES})

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74400.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74400.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74400.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++題目

1、內存管理 1.內存模型 棧:在執行函數時&#xff0c;函數內局部變量的存儲單元都可以在棧上創建&#xff0c;函數執行結束時這些存儲單元自動被釋放。 堆&#xff1a;就是那些由new分配的內存塊&#xff0c;其釋放由程序員控制&#xff08;一個new對應一個delete&#xff09…

天地圖InfoWindow插入React自定義組件

截至2025年03月21日天地圖的Marker不支持添加Label; 同時Label和Icon是不支持自定義HTMLElement只支持String&#xff1b;目前只有InfoWindow支持自定義HTMLElement; 效果圖 React核心api import ReactDOM from react-dom/client const content document.createElement(div);…

Java并發編程面試匯總

Java并發編程 一、 基礎概念1. 進程與線程的區別是什么&#xff1f;2. 創建線程的幾種方式&#xff1f;3. 線程的生命周期&#xff08;狀態&#xff09;有哪些&#xff1f;4. 什么是守護線程&#xff08;Daemon Thread&#xff09;&#xff1f;5. 線程優先級&#xff08;Priori…

【STM32】第一個工程的創建

目錄 1、獲取 KEIL5 安裝包2、開始安裝 KEIL52.1、 激活2.2、安裝DFP庫 3、工程創建4、搭建框架5、開始編寫代碼 1、獲取 KEIL5 安裝包 要想獲得 KEIL5 的安裝包&#xff0c;在百度里面搜索“KEIL5 下載”即可找到很多網友提供的下載文件&#xff0c;或者到 KEIL 的官網下載&a…

動態規劃~01背包問題

01背包問題 經典的0 - 1背包問題的解決方案。 二維數組的版本 代碼功能概述 0 - 1背包問題指的是有 n 個物品和一個容量為 m 的背包&#xff0c;每個物品有對應的體積 v[i] 和價值 w[i]&#xff0c;需要從這些物品里挑選若干個放入背包&#xff0c;讓背包內物品的總價值達到最…

深入理解Java享元模式及其線程安全實踐

引言 在軟件系統中&#xff0c;當需要處理海量細粒度對象時&#xff0c;直接創建大量實例可能會導致內存消耗激增和性能下降。享元模式&#xff08;Flyweight Pattern&#xff09;通過共享對象內部狀態&#xff0c;成為解決這類問題的經典方案。然而在多線程環境下&#xff0c…

1、mysql基礎篇--概述

關系型數據庫&#xff08;RDBMS&#xff09; 概念特點&#xff1a;數據模型&#xff1a; 概念 建立在關系模型基礎上&#xff0c;有多張表相互連接的二維表組成的數據庫 特點&#xff1a; 1、使用表存儲&#xff0c;格式統一&#xff0c;便于維護 2、使用sql語言操作&#…

如何提升庫存系統的高并發和穩定性:算法與設計模式

庫存系統是企業運營的核心模塊&#xff0c;尤其是在電商、零售和供應鏈管理中&#xff0c;系統的高并發和穩定性直接影響訂單處理的準確性和效率。面對海量訂單、復雜的庫存管理需求&#xff0c;如何在高并發環境下確保庫存數據的準確性和系統的穩定性&#xff1f;本文將從架構…

【多線程】synchronized底層實現的方式

前言 在java 開發中對于鎖的應用非常的常見&#xff0c;如果對于什么時候該用什么鎖&#xff0c;以及鎖實現的原理有所不知道的&#xff0c;或者面試過程中面試官問你不知道怎么回答的&#xff0c;歡迎來看下面的文章 1、synchronized和ReentrantLock的區別 2、synchronized的…

Pytorch中Tensorboard的學習

1、Tensorboard介紹 TensorBoard 是 TensorFlow 開發的一個可視化工具&#xff0c;用于幫助用戶理解和調試機器學習模型的訓練過程。盡管它最初是為 TensorFlow 設計的&#xff0c;但通過 PyTorch 的 torch.utils.tensorboard 模塊&#xff0c;PyTorch 用戶也可以方便地使用 Te…

ETL 自動化:提升數據處理效率與準確性的核心驅動力

在數字化轉型的浪潮中&#xff0c;數據已成為企業戰略資產&#xff0c;高效處理數據的能力直接關系到企業的競爭力。ETL&#xff08;Extract, Transform, Load&#xff09;自動化作為數據處理領域的關鍵技術&#xff0c;正逐漸成為企業在數據時代脫穎而出、實現高效運營與精準決…

std::endl為什么C++ 智能提示是函數?

在使用vscode 的C智能提示后&#xff0c;輸入endl 后&#xff0c;提示的卻是std::endl(basic_ostream<CharT, Traits> &os), 感覺比較奇怪&#xff0c;各種代碼里都是直接用的std::endl 啊&#xff0c; 這里怎么變成函數了呢&#xff1f; 在 C 中&#xff0c;std::en…

簡潔、實用、無插件和更安全為特點的WordPress主題

簡站WordPress主題是一款以簡潔、實用、無插件和更安全為特點的WordPress主題&#xff0c;自2013年創立以來&#xff0c;憑借其設計理念和功能優勢&#xff0c;深受用戶喜愛。以下是對簡站WordPress主題的詳細介紹&#xff1a; 1. 設計理念 簡站WordPress主題的核心理念是“崇…

數據結構篇:空間復雜度和時間復雜度

目錄 1.前言&#xff1a; 1.1 學習感悟 1.2 數據結構的學習之路(初階) 2.什么是數據結構和算法 2.1 數據結構和算法的關系 2.2 算法的重要性 2.3 如何衡量算法的好壞 3.時間復雜度 3.1 時間復雜度的概念 3.2 大O的漸進表示法 O() 4.空間復雜度 5. 常見的時間復雜度和…

node-ddk,electron,截屏封裝(js-web-screen-shot)

node-ddk 截屏封裝(js-web-screen-shot) https://blog.csdn.net/eli960/article/details/146207062 也可以下載demo直接演示 http://linuxmail.cn/go#node-ddk 感謝/第三方 本截屏工具, 使用的是: js-web-screen-shot https://www.npmjs.com/package/vue-web-screen-shot…

泰坦軍團攜手順網旗下電競連鎖品牌樹呆熊 共創電競新紀元

在電競行業的浪潮中&#xff0c;品牌之間的戰略合作愈發成為推動市場前行的重要動力。最近&#xff0c;電競顯示器領域領軍品牌泰坦軍團高層領導出席順網旗下電競連鎖品牌樹呆熊十周年盛典。會議現場&#xff0c;雙方高層領導宣布泰坦軍團與樹呆熊正式達成戰略合作伙伴關系。 在…

HandyJSON原理

HandyJSON 的優勢 JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式, 應用廣泛. 在 App 的使用過程中, 服務端給移動端發送的大部分都是 JSON 數據, 移動端需要解析數據才能做進一步的處理. 在解析JSON數據這一塊, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …

信號的產生和保存

信號的產生 信號就是操作系統對用戶操作做出的反應&#xff0c;但它的本質就是往操作系統寫入信號&#xff0c;這是由操作系統的結構決定的。通過修改比特位來告訴操作系統接收信號和傳了幾號信號。 也正是因為我們身為用戶無法親自修改內核數據&#xff0c;所以我們需要通過操…

在C++ Qt中集成Halcon窗口并實現跨平臺兼容和大圖加載

目錄 1. Halcon窗口嵌入Qt Widget 2. 處理大圖加載 3. 多線程優化顯示 4. 跨平臺兼容性 1. Halcon窗口嵌入Qt Widget 將Halcon的HWindow控件嵌入到Qt的QWidget容器中,利用系統原生句柄實現跨平臺。 #include <HalconCpp.h> #include <QWidget>class HalconWi…

深度學習技術與應用的未來展望:從基礎理論到實際實現

深度學習作為人工智能領域的核心技術之一&#xff0c;近年來引起了極大的關注。它不僅在學術界帶來了革命性的進展&#xff0c;也在工業界展現出了廣泛的應用前景。從圖像識別到自然語言處理&#xff0c;再到強化學習和生成對抗網絡&#xff08;GAN&#xff09;&#xff0c;深度…