当前位置: 首页 > news >正文

[mysql 基于C++实现数据库连接池 连接池的使用] 持续更新中

目背景
常见的MySQL、Oracle、SQLServer等数据库都是基于C/S架构设计的,即(客户端/服务器)架构,也就是说我们对数据库的操作相当于一个客户端,这个客户端使用既定的API把SQL语句通过网络发送给服务器端,MySQL Server执行完SQL语句后将结果通过网络返回客户端。通过网络通信的话就要涉及到TCP/IP协议里的“三次握手”、“四次挥手”等,大量访问时,每一个用户的请求都会对应一次“三次握手”、“四次挥手”的过程,这个性能的消耗是相当严重的;
对于数据库本质上是对磁盘的操作,如果对数据库的访问过多,即(I/O)操作过多,会出现访问瓶颈。
而常见的解决数据库访问瓶颈的方法有两种:

一、为减少磁盘 I/O的次数,在数据库和服务器的应用中间加一层 缓存数据库(例如:Redis、Memcache);
二、增加 连接池,来减少高并发情况下大量 TCP三次握手、MySQL Server连接认证、MySQL Server关闭连接回收资源和TCP四次挥手 所耗费的性能。

mysqlconn.hpp 实现连接 增删改查操作

#include <mysql/mysql.h>
#include <iostream>
#include <string>
#include <ctime>
#include <chrono>
#include <memory> #define INFO    1
#define WARNING 2
#define ERROR   3
#define FATAL   4#define LOG(level, message) Log(#level, message, __FILE__, __LINE__)void Log(std::string level, std::string message, std::string file_name, int line)
{std::cout<<"["<<level<<"]["<<time(nullptr)<<"]["<<message<<"]["<<file_name<<"]["<<line<<"]"<<std::endl;
}class mysqlconn{private:MYSQL *m_conn = nullptr;MYSQL_RES* m_res = nullptr;//查询结果集MYSQL_ROW m_row;//记录结构体void freeResult(){if(m_res){mysql_free_result(m_res);m_res = nullptr;}}std::chrono::steady_clock::time_point m_aliveTime;public:mysqlconn(){//获取一个MYSQL句柄m_conn = mysql_init(nullptr);//设置字符集mysql_set_character_set(m_conn,"utf8");}~mysqlconn(){freeResult();if(m_conn != nullptr){mysql_close(m_conn);}}bool query(std::string sql){freeResult();if(mysql_query(m_conn, sql.c_str())){return false;}m_res = mysql_store_result(m_conn);return true;}//更新 修改 删除bool update(std::string sql){return mysql_query(m_conn, sql.c_str());}//连接指定的数据库bool connect(std::string ip, std::string user, std::string passwd, std::string dbName,  unsigned int port){return mysql_real_connect(m_conn, ip.c_str(), user.c_str(), passwd.c_str(), dbName.c_str(), port,nullptr,0) != nullptr;}//遍历得到的结果集bool next(){if(m_res != nullptr){m_row = mysql_fetch_row(m_res);  //获取一行if(m_row != nullptr){return true;}}return false;}//获取结果集里的值std::string value(int index){int rowCount = mysql_num_fields(m_res);  //返回结果集中字段数目if(index >= rowCount || index < 0){return std::string();}char* ans = m_row[index];unsigned long length = mysql_fetch_lengths(m_res)[index];return std::string(ans,length);		}//事务处理提交方式bool transaction(){return mysql_autocommit(m_conn,false);}//事务提交bool commit(){return mysql_commit(m_conn);}//事务回滚bool rollback(){return mysql_rollback(m_conn);}//更新空闲时间点void refreshAliveTime(){m_aliveTime = std::chrono::steady_clock::now();}//计算连接空闲时长long long getAliveTime(){std::chrono::duration<double> diff = std::chrono::steady_clock::now() - m_aliveTime;       //nanosecods 纳秒return diff.count();}};

connpool.hpp 连接池

#include <mutex>
#include <condition_variable>
#include <queue>
#include <fstream>
#include <thread>#include "mysqlconn.hpp"class ConnectionPool
{private:std::string m_user;std::string m_passwd;std::string m_ip;std::string m_dbName;unsigned short m_port;//连接的上限和下限,自动维护线程池的连接数int m_minSize;int m_maxSize;//连接的超时时长int m_timeout;int m_maxIdleTime;//线程同步  std::mutex m_mutexQ;                     //互斥锁std::condition_variable m_cond;          //条件变量std::queue<mysqlconn *> m_connectionQ;    //共享资源public://对外接口,获取线程池//静态局部变量是线程安全的static ConnectionPool  *getConnectPool()    {static ConnectionPool pool;return &pool;}//获取线程池中的连接std::shared_ptr<mysqlconn>  getConnection(){//需要操作共享资源std::unique_lock<std::mutex> locker(m_mutexQ);//判断连接池队列为空while(m_connectionQ.empty()){if(std::cv_status::timeout == m_cond.wait_for(locker, std::chrono::milliseconds(m_timeout))){if(m_connectionQ.empty()){continue;}}}//自定义shared_ptr析构方法,重新将连接放回到连接池中,而不是销毁std::shared_ptr<mysqlconn> connptr(m_connectionQ.front(),[this](mysqlconn *conn){std::unique_lock<std::mutex> locker(m_mutexQ);conn->refreshAliveTime();m_connectionQ.push(conn);	});//弹出,放到了队尾m_connectionQ.pop();m_cond.notify_all();return connptr;}//防止外界通过拷贝构造函数和移动拷贝构造函数ConnectionPool(const ConnectionPool &obj) = delete;ConnectionPool& operator=(const ConnectionPool& obj) = delete;~ConnectionPool(){while(!m_connectionQ.empty()){mysqlconn *conn = m_connectionQ.front();m_connectionQ.pop();delete conn;}}
private://构造函数私有化ConnectionPool(){//加载配置文件if(!parseJsonFile()){return;}//创建最少连接数for(int i=0;i<m_minSize;++i){addConnect();}//创建子线程用于检测并创建新的连接std::thread producer(&ConnectionPool::produceConnection,this);//销毁连接,检测并销毁连接std::thread recycler(&ConnectionPool::recycleConnection,this);//设置线程分离producer.detach();recycler.detach();}//解析配置文件bool parseJsonFile(){    //可以通过配置文件配置数据 这里写死 m_ip      = "127.0.0.1";m_user    = "pig";m_passwd  = "test1234";m_dbName  = "test";m_port    = 3306;m_minSize = 10;m_maxSize = 100;m_timeout = 10;m_maxIdleTime = 20;return true;}//任务函数void produceConnection()   //生产数据库连接{//通过轮询的方式不断的去检测while(true) {//操作共享资源,需要加锁std::unique_lock<std::mutex> locker(m_mutexQ);//判断连接数是否达到容量,如果大于等于容量则需要阻塞一段时间while (m_connectionQ.size() >= m_maxSize)   {m_cond.wait(locker);}addConnect();m_cond.notify_all();        //唤醒消费者}}void recycleConnection()   //销毁数据库连接{while(true){//休眠一定的时长std::this_thread::sleep_for(std::chrono::milliseconds(500));std::unique_lock<std::mutex> locker(m_mutexQ);//让线程池中最少保持用于 m_minSize个线程while(m_connectionQ.size() > m_minSize){mysqlconn *recyConn = m_connectionQ.front();//如果超时则销毁if(recyConn->getAliveTime() >= m_maxIdleTime){m_connectionQ.pop();delete recyConn;} else{break;}}}}void addConnect()         //添加连接{mysqlconn *conn = new mysqlconn;conn->connect(m_ip,m_user,m_passwd,m_dbName,m_port);conn->refreshAliveTime();m_connectionQ.push(conn);}};

main.cpp 测试主函数 单线程 连接池 多线程连接池

#include "connpool.hpp"void pthread1_no_pool()
{clock_t begin = clock();std::unique_ptr<mysqlconn> sp = std::make_unique<mysqlconn>();bool connflag = sp->connect("127.0.0.1","pig","test1234", "test",3306);if(connflag == false) return;for (int i = 0; i < 4 * 1000; ++i){sp->refreshAliveTime();char sql[1024] = { 0 };sprintf(sql, "insert into tb_file values('%d','%s','%s');",i, "pthread1_no_pool", "1.png");auto upflag = sp->update(sql);}clock_t end = clock();std::cout << "pthread1_no_pool:" << (end - begin) << "ms" << std::endl;}void pthread1_use_pool(){ConnectionPool *cp = ConnectionPool::getConnectPool();clock_t begin = clock();std::shared_ptr<mysqlconn> sp = cp->getConnection();for (int i = 0; i < 1000 * 4; ++i){char sql[1024] = { 0 };sprintf(sql, "insert into tb_file(id, name, file) values('%d','%s','%s');",i, "pthread1_use_pool", "1.png");sp->update(sql);}clock_t end = clock();std::cout <<"pthread1_use_pool:" << (end - begin) << "ms" << std::endl;}void pthread4_no_pool()
{clock_t begin = clock();std::thread tt[4];for(int n = 0; n < 4; n++){tt[n] = std::thread([=]{std::unique_ptr<mysqlconn> sp = std::make_unique<mysqlconn>();sp->connect("127.0.0.1","pig","test1234", "test",3306);for (int i = 0; i < 1000 * (n + 1); ++i){sp->refreshAliveTime();char sql[1024] = { 0 };sprintf(sql, "insert into tb_file values('%d','%s','%s');",i, "pthread1_no_pool", "1.png");sp->update(sql);}});}for(int i = 0; i < 4; i++){tt[i].join();}clock_t end = clock();std::cout <<"pthread4_no_pool:" << (end - begin) << "ms" << std::endl;}void work(ConnectionPool *cp , int l){std::shared_ptr<mysqlconn> sp = cp->getConnection();for (int i = l * 1000; i < 1000 * (l + 1); ++i){char sql[1024] = { 0 };sprintf(sql, "insert into tb_file values('%d','%s','%s');",i, "pthread1_use_pool", "1.png");auto upflag = sp->update(sql);if(upflag != 0){std::cout <<"pthread4_use_pool:" << upflag << sql << std::endl;continue;}}
}void pthread4_use_pool()
{ConnectionPool *cp = ConnectionPool::getConnectPool();clock_t begin = clock();std::thread tt[4];for(int i = 0; i < 4; i++){tt[i] = std::thread(work, cp, i);}for(int i = 0; i < 4; i++){tt[i].join();}clock_t end = clock();std::cout <<"pthread4_use_pool:" << (end - begin) << "ms" << std::endl;
}// g++ -o main main.cpp connpool.hpp mysqlconn.hpp -lmysqlclient -std=c++14 -lpthread
int main()
{/*单线程 不使用连接池*///LOG(INFO, "pthread1_no_pool test:");//pthread1_no_pool();/*单线程 使用连接池*///LOG(INFO, "pthread1_use_pool test:");//pthread1_use_pool();/*多线程 不使用连接池*/LOG(INFO, "pthread4_no_pool test:");pthread4_no_pool();/*多线程 使用连接池*///LOG(INFO, "pthread4_use_pool test:");//pthread4_use_pool();return 0;
}

单线程
单线程 无连接池 4000条数据插入
在这里插入图片描述
单线程 连接池 4000条数据插入
在这里插入图片描述
4线程 无连接池
在这里插入图片描述

4线程 连接池
在这里插入图片描述

测试结果 和预期一样 多线程下使用连接池中的连接 比重复建立连接快很多

![结果](https://img-blog.csdnimg.cn/direct/5b15db6b9ade48b5b6f65b061a45b200.png参考
https://zhuanlan.zhihu.com/p/616675628

相关文章:

[mysql 基于C++实现数据库连接池 连接池的使用] 持续更新中

目背景 常见的MySQL、Oracle、SQLServer等数据库都是基于C/S架构设计的&#xff0c;即&#xff08;客户端/服务器&#xff09;架构&#xff0c;也就是说我们对数据库的操作相当于一个客户端&#xff0c;这个客户端使用既定的API把SQL语句通过网络发送给服务器端&#xff0c;MyS…...

【Flink SQL API体验数据湖格式之paimon】

前言 随着大数据技术的普及&#xff0c;数据仓库的部署方式也在发生着改变&#xff0c;之前在部署数据仓库项目时&#xff0c;首先想到的是选择国外哪家公司的产品&#xff0c;比如&#xff1a;数据存储会从Oracle、SqlServer中或者Mysql中选择&#xff0c;ETL工具会从Informa…...

idea导入spring-framework异常:error: cannot find symbol

从github上clone代码spring-framework到本地后导入idea&#xff0c;点击gradle构建后控制台提示异常&#xff1a; 具体异常信息&#xff1a; /Users/ZengJun/Desktop/spring-framework/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java:44: error:…...

Unity坦克大战开发全流程——开始场景——开始界面

开始场景——开始界面 step1&#xff1a;设置UI 反正按照这张图拼就行了 step2&#xff1a;写脚本 前面的拼UI都是些比较机械化的工作&#xff0c;直到这里写代码的时候才真正开始有点意思了&#xff0c;从这里开始&#xff0c;我们就要利用面向对象的思路来进行分析&#xff1…...

【SpringCloud】从实际业务问题出发去分析Eureka-Server端源码

文章目录 前言1.EnableEurekaServer2.初始化缓存3.jersey应用程序构建3.1注册jeseryFilter3.2构建JerseyApplication 4.处理注册请求5.registry&#xff08;&#xff09; 前言 前段时间遇到了一个业务问题就是k8s滚动发布Eureka微服务的过程中接口会有很多告警&#xff0c;当时…...

Java 代理模式

一、代理模式概述 代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问&#xff0c;这样就可以在不修改原目标对象的前提下&#xff0c;提供额外的功能操作&#xff0c;扩展目标对象的功能。 代理模式的主要作用是扩展目标…...

【Java干货教程】JSON,JSONObject,JSONArray类详解

一、定义 JSON&#xff1a;就是一种轻量级的数据交换格式&#xff0c;被广泛应用于WEB应用程序开发。JSON的简洁和清晰的层次结构&#xff0c;易于阅读和编写&#xff1b;同时也易于机器解析和生成&#xff0c;有效的提升网络传输效率&#xff1b;支持多种语言&#xff0c;很多…...

2023年高级软考系统架构师考题参考

对于一些有实践经验的同学来说&#xff0c;感觉不难&#xff0c;但是落笔到纸面上&#xff0c;就差强人意了&#xff0c;平时这方面要多练习&#xff0c;所想所思要落到纸面上&#xff0c;或者表达清晰让别人听懂&#xff0c;不仅是工作中的一个基本素质&#xff0c;也是个非常…...

【c语言】飞机大战(1)

提前准备好游戏要的素材&#xff0c;可以到爱给网去找&#xff0c;飞机大战我们需要的是一个我方战机图片&#xff0c;一个背景图&#xff0c;三个敌方战机的图&#xff0c;我方战机的图片&#xff0c;敌方战机的图片&#xff0c;并且将图片和.cpp放在同一文件夹下. 这里创建.…...

关于 K8s 的一些基础概念整理

〇、前言 Kubernetes&#xff0c;将中间八个字母用数字 8 替换掉简称 k8s&#xff0c;是一个开源的容器集群管理系统&#xff0c;由谷歌开发并维护。它为跨主机的容器化应用提供资源调度、服务发现、高可用管理和弹性伸缩等功能。 下面简单列一下 k8s 的几个特性&#xff1a; 自…...

Node.js-fs、path、http模块

1.初识Node.js 1.1 什么是Node.js 1.2 Node.js中的JavaScript运行环境 1.3 Node.js可以做什么 Node.js 作为一个JavaScript 的运行环境&#xff0c;仅仅提供了基础的功能和 AP1。然而&#xff0c;基于 ode.s 提供的这些基础能&#xff0c;很多强大的工具和框架如雨后春笋&…...

CentOS 安装WebLogic

1.JDK 安装 cd /home/ mkdir java cd java/ tar -zxvf jdk-8u321-linux-x64.tar.gzvim /etc/profile添加以下内容到 /etc/profile JAVA_HOME/home/java/jdk1.8.0_321 CLASSPATH.:$JAVA_HOME/lib.tools.jar PATH$JAVA_HOME/bin:$PATH export JAVA_HOME CLASSPATH PATH刷新配置…...

Linux命令的操作练习

1.创建ss别名&#xff0c;查看长格式详细信息 alias ssls -l 2.创建ss别名&#xff0c;复制boot文件夹下的内容到data文件夹下 alias sscp -r /boot /data 3.删除别名ss unalias ss 4. 复制test文件夹下的passwd文件到qq文件夹下&#xff0c;并改名为ww cp test/pas…...

杰发科技AC7840——EEPROM初探

0.序 7840和7801的模拟EEPROM使用不太一样 1.现象 按照官方Demo&#xff0c;在这样的配置下&#xff0c;我们看到存储是这样的&#xff08;连续三个数字1 2 3&#xff09;。 使用串口工具的多帧发送功能 看不出多少规律 修改代码后 发现如下规律&#xff1a; 前四个字节是…...

WPF 基础入门(简介)

简介 WPF&#xff08;Windows Presentation Foundation&#xff09;是微软推出的基于Windows 的用户界面框架&#xff0c;属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架&#xff0c;真正做到了分离界面设计人员与开发人员的工作&#xff1b;同时它提供了…...

【Unity动画系统】Animator有限状态机参数详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

php获取访客IP、UA、操作系统、浏览器等信息

最近有个需求就是获取下本地的ip地址、网上搜索了相关的教程&#xff0c;总结一下分享给大家、有需要的小伙伴可以参考一下 一、简单的获取 User Agent 信息代码: echo $_SERVER[HTTP_USER_AGENT]; 二、获取访客操作系统信息: /** * 获取客户端操作系统信息,包括win10 * pa…...

基于huffman编解码的图像压缩算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 Huffman编码算法步骤 4.2 Huffman编码的数学原理 4.3 基于Huffman编解码的图像压缩 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..…...

python+django网上购物商城系统o9m4k

语言&#xff1a;Python 框架&#xff1a;django/flask可以定制 软件版本&#xff1a;python3.7.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发工具pycharm/vscode都可以 前端框架:vue.js 系统使用过程主要涉及到管理员和用户两种角色&#xff0c;主要包含个…...

面试题-性能优化

前端项目优化&#xff1a; 一般考虑方面: (挑几点记住) 我们学的: 懒加载: 路由、图片懒加载 骨架屏的使用 压缩文件&#xff1a;可以使用压缩工具&#xff08;如GZIP&#xff09;对页面文件进行压缩&#xff0c;减小文件大小&#xff0c;提高页面加载速度。 减少HTTP请求&a…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...