基于C++“简单且有效”的“数据库连接池”

前言

  • 数据库连接池在开发中应该是很常用的一个组件,他可以很好的节省连接数据库的时间开销;
  • 本文基使用C++实现了一个简单的数据库连接池,代码量只有400行只有,但是压力测试效果很好
  • 欢迎收藏 + 关注,本人将会持续更新后端与AI算法有关知识点

文章目录

      • 连接池功能点介绍
        • 初始连接量
        • 最大连接量
        • 最大空闲时间
        • 连接超时时间
      • 连接池主要包含了以下功能点
      • 代码
      • 压力测试

MySQL 数据库是基于 C/S 模式的,在每一次访问mysql服务器的时候,都需要建立一个TCP连接但是,在高并发情况下,大量的 TCP 三次握手、MySQL Server 连接认证、MySQL Server 关闭连接回收资源和 TCP 四次挥手所耗费的性能事件也是很明显的,故设置连接池就是为了减少这一部分的性能损耗

连接池功能点介绍

初始连接量

表示连接池事先会和 MySQL Serve r创建最小个数的连接,当应用发起 MySQL 访问时,不用再创建和 MySQL Server 新的连接,直接从连接池中获取一个可用的连接即可,使用完成后并不去释放连接,而是把连接再归还到连接池当中

最大连接量

当并发访问MySQL Server的请求增多时,初始连接量已经不够用了,此时会去创建更多的连接给应用去使用,是新创建的连接数量上限是maxSize,不能无限制的创建连接。并且当这些连接使用完之后,再次归还到连接池当中来维护。

最大空闲时间

当访问MySQL的并发请求多了以后,连接池里面的连接数量会动态增加,上限是maxSize 个,当这些连接用完会再次归还到连接池当中。如果在指定的时间内这些新增的连接都没有被再次使用过,那么新增加的这些连接资源就要被回收掉,只需要保持初始连接量个连接即可

连接超时时间

当MySQL的并发请求量过大,连接池中的连接数量已经到达最大数量了,而此时没有空闲的连接可供使用,那么此时应用从连接池获取连接无法成功,它通过阻塞的方式获取连接的时间,如果超过一个时间,那么获取连接失败

连接池主要包含了以下功能点

  • 单例模式设置连接池;
  • 向用户提供一个接口,可以从池中拿到一个数据库连接;
  • 采用生产者-消费者模式,池用队列作为缓冲区,实现生成连接和拿取连接;
  • 采用锁,在创建、拿取连接中进行加锁;
  • 采用智能指针管理从队列中获取的连接,并且采用lambda实现智能指针的析构函数,将连接从新放回队列中;
  • 设置生产连接、回收连接线程,并且驻留后台,作为守护线程;
  • 采用原子变量才记录当前池中的连接数。

创建目录如下

在这里插入图片描述

代码

logger.h

#ifndef PUBLIC_H_
#define PUBLIC_H_#include <iostream>// 作用:封装简单的LOG
#define LOGGER(str) std::cout << "====》" << __LINE__ << " time: " << __TIME__ << " message: " << str << std::endl;#endif // !PUBLIC_H_

connection.h

#ifndef CONNECTION_H_
#define CONNECTION_H_#include <mysql/mysql.h>
#include <ctime>
#include <string>/*
功能:初始化数据库连接释放连接连接数据库查询mysql修改数据库数据刷新/设置空闲时间的起始时间点返回空闲时间
*/class Connection
{
public:// 初始化数据库连接Connection();// 释放连接~Connection();// 连接数据库bool connectionSqlBase(std::string ip, unsigned int port, std::string user, std::string passward, std::string dbName);// 查询mysqlMYSQL_RES* query(std::string sql);// 修改数据库数据bool modify(std::string sql);// 刷新/设置空闲时间的起始时间点void setStartActivateTime();// 返回空闲时间clock_t getActivateTime();
private: MYSQL* m_sqlConn{};   // 连接mysql服务器clock_t m_activateTime;  // 记录空闲时间的起始点
};#endif // !CONNECTION_H_

connection.cpp

#include "connection.h"
#include "logger.h"// 初始化数据库连接
Connection::Connection()
{m_sqlConn = mysql_init(nullptr);if(m_sqlConn == nullptr) {LOGGER("mysql init false !!!");return;}
}
// 释放连接
Connection::~Connection()
{mysql_close(m_sqlConn);
}
// 连接数据库
bool Connection::connectionSqlBase(std::string ip, unsigned int port, std::string user, std::string passward, std::string dbName)
{if(nullptr == mysql_real_connect(m_sqlConn, ip.c_str(), user.c_str(), passward.c_str(), dbName.c_str(), port, NULL, 0)) {LOGGER("mysql connects error!!");return false;}return true;
}
// 查询mysql
MYSQL_RES* Connection::query(std::string sql)
{int res = mysql_query(m_sqlConn, sql.c_str());if(0 != res) {LOGGER("sql query false!!!");return nullptr;}return mysql_use_result(m_sqlConn);
}
// 修改数据库数据
bool Connection::modify(std::string sql)
{int res = mysql_query(m_sqlConn, sql.c_str());if(0 != res) {LOGGER("sql update/insert/select false!!!");return false;}return true;
}
// 刷新/设置空闲时间的起始时间点
void Connection::setStartActivateTime()
{m_activateTime = clock();
}
// 返回空闲时间
clock_t Connection::getActivateTime()
{return clock() - m_activateTime;
}

dbConnectionPool.h

#ifndef DBCONNECTION_H_
#define DBCONNECTION_H_#include "connection.h"
#include <mysql/mysql.h>
#include <queue>
#include <string>
#include <condition_variable>
#include <atomic>
#include <memory>// 核心:生产者、消费者模式class DbConnPool
{
public: // 单例模式static DbConnPool* getDbConnPool();// 对外提供接口: 获取连接的数据库,通过智能指针回收std::shared_ptr<Connection> getMysqlConn();// 测试// void test()// {//     readConfigurationFile();// }private: // 单例模型:构造函数私有化, 目的:创建最小连接数量DbConnPool();DbConnPool(const DbConnPool&) = delete;DbConnPool operator=(const DbConnPool&) = delete;// 读取配置文件bool readConfigurationFile();// 如果没有Mysql连接了,则产生新连接,这个线程驻留后台(像守护线程一样)void produceNewConn();// 如果队列线程 > initSize, 且空闲时间大于最大空闲时间,则回收void recycleConn();private:// MYSQL连接信息std::string m_ip;unsigned int m_port;std::string m_username;std::string m_password;std::string m_dbname;// 数据库连接池信息int m_initSize;int m_maxSize;int m_maxFreeTime;int m_maxConnTime;// 生产者、消费者共享内存:获取连接std::queue<Connection*> m_connQueue;// 存储当前存储到队列中存储的数量std::atomic_int m_conntionCnt{};// 锁std::mutex m_queueMuetx;// 生产者、消费者:产生连接和取连接std::condition_variable m_cv;  // 用于生产线程和消费线程之间的通信
};#endif // !DBCONNECTION_H_

dbConnectionPool.cpp

#include "dbConnectionPool.h"
#include "connection.h"
#include "logger.h"#include <iostream>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <thread>
#include <functional>
#include <chrono>// 创建连接:new,但是拿取连接后是交给shared_ptr// 单例模式
DbConnPool* DbConnPool::getDbConnPool()
{// 最简单的方式:staticstatic DbConnPool pool;return &pool;
}// 对外提供接口: 获取连接的数据库,通过智能指针回收
std::shared_ptr<Connection> DbConnPool::getMysqlConn()
{std::unique_lock<std::mutex> lock(m_queueMuetx);// 判断队列是否为空while(m_connQueue.empty()) {// 队列为空,则等待 最大连接时间, 即这个时候客户端请求连接,但是池里没有连接了,则会等待,如果超过了最大时间,则:连接失败if(std::cv_status::timeout == m_cv.wait_for(lock, std::chrono::seconds(m_maxConnTime))) {// 再次判断是否为空if(m_connQueue.empty()) {LOGGER("no conntion !!!!");return nullptr;}}}/*从队列中获取一个连接,交给**智能指针管理**注意:删除连接有回收线程监控,而这里获取的连接使用完后,需要还给**队列中**,所以**需要重写智能指针的回收函数***/std::shared_ptr<Connection> sp(m_connQueue.front(), [&](Connection* pconn){// 注意,这里需要加锁std::unique_lock<std::mutex> lock(m_queueMuetx);pconn->setStartActivateTime();m_connQueue.push(pconn);  // 入队m_conntionCnt++;   // +1});// 弹出队列m_connQueue.pop();m_conntionCnt--;   // -1return sp;
}// 单例模型:构造函数私有化
DbConnPool::DbConnPool()
{if(readConfigurationFile() == false) {return;}std::unique_lock<std::mutex> lock(m_queueMuetx);for(int i = 0; i < m_maxSize; i++) {Connection* newConn = new Connection();newConn->connectionSqlBase(m_ip, m_port, m_username, m_password, m_dbname);newConn->setStartActivateTime();   // 设置 空闲时间 的起始点m_connQueue.push(newConn);      // 入队m_conntionCnt++;     // 存储到队列中数据+1}// 开启线程:检查是否需要需要**新创建连接**std::thread produce(std::bind(&DbConnPool::produceNewConn, this));produce.detach();   // 驻留后台// 开启线程,检查是否需要**删除连接**std::thread search(std::bind(&DbConnPool::recycleConn, this));search.detach();    // 驻留后台
}// 读取配置文件
bool DbConnPool::readConfigurationFile()
{FILE* fp = fopen("./mysql.ini", "r");if(fp == nullptr) {LOGGER("mysql.ini open false!!");return false;}char buff[BUFSIZ] = { 0 };while(!feof(fp)) {// clearmemset(buff, 0, sizeof(buff));// 读取fgets(buff, BUFSIZ, fp);std::string str = buff;// 判空if(str.empty()) {continue;}// 截断int idx = str.find('=', 0);if(idx == -1) {continue;}int end = str.find('\n', idx);std::string key = str.substr(0, idx);std::string value = str.substr(idx + 1, end - idx - 1);//std::cout << "key: " << key << " value: " << value << std::endl;if(key == "ip") {m_ip = value;} else if(key == "port") {m_port = atoi(value.c_str());} else if(key == "username") {m_username = value;} else if(key == "password") {m_password = value;} else if(key == "dbname") {m_dbname = value;} else if(key == "initSize") {m_initSize = atoi(value.c_str());} else if(key == "maxSize") {m_maxSize = atoi(value.c_str());} else if(key == "maxFreeTime") {m_maxFreeTime = atoi(value.c_str());} else if(key == "maxConnTime") {m_maxConnTime = atoi(value.c_str());}}std::cout << m_ip << " " << m_port << " " << m_username << " " << m_password << " " << m_dbname << " " << m_initSize << " " << m_maxSize << " " << m_maxFreeTime << " " << m_maxConnTime << std::endl;return true;
}// 如果池里没有Mysql连接了,则产生新连接,这个线程驻留后台(像守护线程一样)
/*
实现思路:设置一个循环:循环检查如果队列不为空,则条件变量一直等待
*/
void DbConnPool::produceNewConn()
{for(;;) {std::unique_lock<std::mutex> lock(m_queueMuetx);while(!m_connQueue.empty()) {m_cv.wait(lock);    // 条件变量一直等待}// 这个时候,队列为空,从新创建连接for(int i = 0; i < m_maxSize; i++) {Connection* newConn = new Connection();newConn->setStartActivateTime();   // 刷新时间m_connQueue.push(newConn);m_conntionCnt++;   // +1}// 通知等待线程m_cv.notify_all();}
}// 如果队列线程 > initSize, 且空闲时间大于最大空闲时间,则回收
void DbConnPool::recycleConn()
{for(;;) {std::unique_lock<std::mutex> lock(m_queueMuetx);while(m_conntionCnt > m_initSize) {Connection* conn = m_connQueue.front();// 超过最大空闲时间if((static_cast<double>(conn->getActivateTime()) / CLOCKS_PER_SEC) > m_maxFreeTime) {m_connQueue.pop();m_conntionCnt--;delete conn;} else {   // 对头没超过,则直接退出break;}}}
}

压力测试

分别插入10000条数据,对没有使用连接池和使用连接池分别进行测试。

#include "dbConnectionPool.h"
#include "connection.h"
#include <iostream>
#include <mysql/mysql.h>
#include <chrono>int main() {auto start = std::chrono::high_resolution_clock::now(); // 获取当前时间点for(int i = 0; i < 10000; i++) {
#if 1DbConnPool* pool = DbConnPool::getDbConnPool();std::shared_ptr<Connection> conn = pool->getMysqlConn();char sql[1024] = { 0 };sprintf(sql, "insert into temp(name, age, sex) values('%s', '%d', '%s');", "yxz", 18, "man");conn->modify(sql);
#elif  0Connection conn;conn.connectionSqlBase("127.0.0.1", 3306, "root", "wy2892586", "test");char sql[1024] = { 0 };sprintf(sql, "insert into temp(name, age, sex) values('%s', '%d', '%s');", "yxz", 18, "man");conn.modify(sql);
#endif}auto end = std::chrono::high_resolution_clock::now(); // 获取结束时间点std::chrono::duration<double, std::milli> duration = end - start; // 计算持续时间,并转换为毫秒std::cout << "Time: " << duration.count() << " ms" << std::endl;return 0;
}

结果如下

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24355.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何通过提示词更好地利用AI

如何通过提示词工程释放AI的全部潜力&#xff1a;7个深度优化技巧 前言&#xff1a;为什么提示词决定AI的输出质量&#xff1f; 在人工智能对话系统的使用中&#xff0c;提示词&#xff08;Prompt&#xff09;就像开启宝藏的密码钥匙。研究表明&#xff0c;优化后的提示词可使…

【C语言】指针笔试题

前言&#xff1a;上期我们介绍了sizeof与strlen的辨析以及sizeof&#xff0c;strlen相关的一些笔试题&#xff0c;这期我们主要来讲指针运算相关的一些笔试题&#xff0c;以此来巩固我们之前所学的指针运算&#xff01; 文章目录 一&#xff0c;指针笔试题1&#xff0c;题目一…

电子科技大学考研复习经验分享

电子科技大学考研复习经验分享 本人情况&#xff1a;本科就读于电科软院&#xff0c;24年2月开始了解考研&#xff0c;24年3月开始数学&#xff0c;9月决定考本院&#xff08;开始全天候图书馆学习&#xff09;并开始专业课学习&#xff0c;11月底开始政治学习&#xff0c;最后…

c#笔记-基础知识

目录 整数 浮点 decimal bool 引用 object string 值 枚举 关键字 enum 类型转换 隐式转换 ​编辑 显式转换 装箱&#xff0c;拆箱 常量 表达式 ​编辑 字符串的使用 比较字符串 字符格式化 ​编辑 截取字符串 字符串的分割 插入字符串 删除字符串 …

表单制作代码,登录动画背景前端模板

炫酷动效登录页 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS来实现一个“表单制作代码,登录动画背景前端模板”。该素材呈现了数据符号排版显示出人形的动画效果,新颖有…

基于Hadoop的汽车大数据分析系统设计与实现【爬虫、数据预处理、MapReduce、echarts、Flask】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍爬虫数据概览HIve表设计Cars Database Tables 1. cars_data2. annual_sales_volume3. brand_sales_volume4. city_sales_volume5. sales_volume_by_year_and_brand6. sales_distri…

C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 1、本节实现的内容 上一节课,我们在Blend软件中导出经纬球模型时,遇到了经纬球法线导致我们在游戏中模型光照显示问题,我们在Blender软件中可以通过…

SOME/IP-SD -- 协议英文原文讲解2

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.2.2 S…

C++复习专题——泛型编程(模版),包括模版的全特化和偏特化

1.泛型编程 在未接触模版前&#xff0c;如果我们想实现一个通用的交换函数&#xff0c;那么我们可以通过函数重载来实现 void Swap(int &x,int &y) {int z x;x y;y z; } void Swap(float &x,float &y) {int z x;x y;y z; } void Swap(double &x,dou…

Docker入门

注意&#xff1a; 本文内容于 2025-02-23 01:18:58 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;Docker入门。感谢您的关注与支持&#xff01; 在日常开发中&#xff0c;其实我这里是没有使用 D…

haproxy基本配置详解

一、haproxy基础配置 官方文档&#xff1a;HAProxy version 2.2.22 - Configuration Manual HAProxy 的配置文件haproxy.cfg由两大部分组成&#xff0c;分别是global和proxies部分。 global&#xff1a;全局配置段 进程及安全配置相关的参数性能调整相关参数Debug参数 pro…

nodejs:express + js-mdict 作为后端,vue 3 + vite 作为前端,在线查询英汉词典

向 doubao.com/chat/ 提问&#xff1a; node.js js-mdict 作为后端&#xff0c;vue 3 vite 作为前端&#xff0c;编写在线查询英汉词典 后端部分&#xff08;express js-mdict &#xff09; 1. 项目结构 首先&#xff0c;创建一个项目目录&#xff0c;结构如下&#xff1…

网络原理--TCP的特性

TCP报文的结构&#xff1a; TCP的报头前20字节是固定长度&#xff0c;也可以通过“选项”来增加。 一、用来确保可靠性&#xff0c;最核心的机制&#xff0c;称为“确认应答” 引入一个情景&#xff1a; A向B询问cat和dog的意思&#xff1a; 这种情况是理想情况&#xff0c;…

Java 大视界 -- 深度洞察 Java 大数据安全多方计算的前沿趋势与应用革新(52)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

【HTML— 快速入门】HTML 基础

准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器&#xff0c;我们在写前端代码时&#xff0c;使用 Sublime Text 打开比使用记事本打开&#xff0c;得到的代码体验更好&#xff0c;比 vscode…

深圳南柯电子|医疗设备EMC测试整改检测:零到一,保障医疗安全

在当今医疗科技飞速发展的时代&#xff0c;医疗设备的电磁兼容性&#xff08;EMC&#xff09;已成为确保其安全、有效运行的关键要素之一。EMC测试整改检测不仅关乎设备的性能稳定性&#xff0c;更是保障患者安全、避免电磁干扰引发医疗事故的重要措施。 一、医疗设备EMC测试整…

Tomcat安装与环境变量配置(图文详解)

一、前置条件 tomcat依赖java环境&#xff0c;需要先安装java环境。 jdk环境变量配置可参考&#xff1a;win7、win10系统JDK环境变量配置-CSDN博客 二、Tomcat的下载与安装 1.进入Tomcat官网&#xff1a; https://tomcat.apache.org/ 2.在该网页左侧栏红框标记的download…

网络安全之Web后端PHP

目录 一、PHP基础语法 1.PHP基础 &#xff08;1&#xff09;php的优点 &#xff08;2&#xff09;PhpStorm的优点 2.PHP基本语法 3.PHP变量 4.PHP运算符 二、PHP流控与数组 1.php流程控制语句以及循环 &#xff08;1&#xff09;if 语句 &#xff08;2&#xff09;if…

Python 批量横屏转竖屏视频处理工具

Python 批量横屏转竖屏视频处理工具 相关资源文件已经打包成EXE文件&#xff0c;可双击直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Python相关程序案例&#xff0c;秉着开源精神的想法&#xff0c;望大家喜欢&…

ESP32S3:解决RWDT无法触发中断问题,二次开发者怎么才能使用内部RTC看门狗中断RWDT呢?

目录 基于ESP32S3:解决RWDT无法触发中断问题引言解决方案1. 查看报错日志2. 分析报错及一步一步找到解决方法3.小结我的源码基于ESP32S3:解决RWDT无法触发中断问题 引言 在嵌入式系统中,RWDT(看门狗定时器)是确保系统稳定性的重要组件。然而,在某些情况下,RWDT可能无法…