从零开始实现 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.mzph.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;深度…