[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

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

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

相关文章

Animate 2024(Adobe an2024)

Animate 2024是一款由Adobe公司开发的动画和互动内容创作工具&#xff0c;是Flash的演进版本。Animate 2024为设计师和开发者提供了更丰富的功能&#xff0c;让他们能够创建各种类型的动画、交互式内容和多媒体应用程序。 Animate 2024具有以下特点&#xff1a; 强大的设计工…

一个静态网站可以增加什么第三方功能/服务

一个静态网站&#xff0c;无后台功能&#xff0c;怎么增加一些实用功能呢&#xff1f;我们来看看一些免费的第三方服务。 静态页面寄存 Gitee pages/Github pages 都可以&#xff0c;绑定一个域名&#xff0c;版本一提交&#xff0c;直接发布有效果。 评论 每个 URL 页面都…

华为hcia之ipv6实验手册

R3: dhcp enable ipv6 dhcpv6 pool test address prefix 2000:23::/64 excluded-address 2000:23::2 dns-server 2000:23::2 interface GigabitEthernet0/0/0 ipv6 enable ipv6 address 2000:12::2/64 ipv6 address auto link-local undo ipv6 nd ra halt //无状态配置 inter…

SpringBoot用JDK1.8的依赖设置pom.xml

pom.xml的修改主要是两个地方&#xff1a; 1.修改springframework的版本为2.5.0&#xff0c;版本太高可能和其他插件搭配有冲突&#xff1b; 2.Java的版本修改成8&#xff0c;也就是对应JDK1.8。

uni-app API接口扩展组件(uni-ui)

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

云原生|kubernetes|kubernetes资源备份和集群迁移神器velero的部署和使用

前言&#xff1a; kubernetes集群需要灾备吗&#xff1f;kubernetes需要迁移吗&#xff1f; 答案肯定是需要的 那么&#xff0c;如何做kubernetes灾备和迁移呢&#xff1f;当然了&#xff0c;有很多的方法&#xff0c;例如&#xff0c;自己编写shell脚本&#xff0c;或者使用…

Geotrust的rapidssl证书产品

Geotrust成立于2001年&#xff0c;现在是Digicert旗下的SSL数字证书品牌&#xff0c;经营范围遍布全球&#xff0c;为众多用户提供网站数据加密和身份认证服务。RapidSSL是Geotrust旗下的子品牌证书&#xff0c;主要有两款入门级的SSL数字证书&#xff0c;今天就随SSL盾小编了解…

RM3100 stm32驱动(硬件i2c)

目录 RM3100接线HAL库I2C函数HAL_I2C_Mem_ReadHAL_I2C_Mem_WriteHAL_I2C_Master_Transmit / HAL_I2C_Master_Receive例子 HSHAKE寄存器 cubemx配置RM3100寄存器驱动最终效果 RM3100接线 原理图 SA0 SA1接地&#xff0c;此时i2c设备地址为0100000&#xff0c;即0x20 如果SA0接…

大创项目推荐 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

springBoot2.3-基本介绍及入门案例

本次学习雷丰阳springBoot(2.3版本)。建议先修ssm 一、SpringBoot基本介绍 springBoot是当今最为流行的java开发框架。 1、springBoot的底层是spring&#xff0c; 因此继承了spring的粘合其他框架的能力。 2、本质上还是其他框架包括spring在工作 , springBoot起到一个整合其他…

uni-app page新建以及page外观配置

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

暗光增强——URetinex-Net网络推理测试

目录 一、URetinex-Net网络二、源码包三、测试3.1 修改参数3.2 测试结果 四、推理速度五、总结 一、URetinex-Net网络 URetinex-Net 设计了三个基于学习的模块&#xff0c;分别负责数据相关的初始化、高效的展开优化和用户指定的光照增强。由于 URetinex-Net 的推理速度非常快…

【温故而知新】vue运用之探讨下单页面应用(SPA)与多页面应用(MPA)

一、概念 1.单页面应用SPA(Single page application) Vue单页面应用是一种采用Vue.js框架开发的Web应用程序,它仅有一个HTML文件,通过前端路由实现页面的切换和渲染。与传统的多页面应用相比,Vue单页面应用在用户体验和开发效率方面有着明显的优势。 在Vue单页面应用中…

与擎创科技共建一体化“数智”运维体系,实现数字化转型

小窗滴滴小编获取最新版公司简介 前言&#xff1a; 哈喽大家好&#xff0c;最近分享的互联网IT热讯大家都挺喜欢&#xff0c;小编看着数据着实开心&#xff0c;感谢大家支持&#xff0c;小编会继续给大家推送。 新岁即将启封&#xff0c;我们一年一期的运维干货年末大讲也要…

Zookeeper-Zookeeper应用场景实战

1. Zookeeper Java客户端实战 ZooKeeper应用的开发主要通过Java客户端API去连接和操作ZooKeeper集群。 可供选择的Java客户端API有&#xff1a; ZooKeeper官方的Java客户端API。 第三方的Java客户端API&#xff0c;比如Curator。 ZooKeeper官方的客户端API提供了基本的操作…

OCP NVME SSD规范解读-4.NVMe IO命令-2

NVMe-IO-3&#xff1a; 由于设备具有掉电保护功能&#xff08;如Power Loss Protection&#xff0c;PLP&#xff09;&#xff0c;因此在以下情况下&#xff0c;性能不应降低&#xff1a; FUA&#xff08;Force Unit Access&#xff09;&#xff1a;是计算机存储设备中的一种命…

ubuntu22.04安装anacoda遇到的坑

这几天把用了3年的windows10换成了ubuntu22.04 各种环境都得配置&#xff0c;本文记录下遇到的坑。 1、anacoda在ubuntu上也可以用官方也提供了安装包&#xff0c;但是没有图形界面&#xff0c;需要以命令行的方式安装和运行配置 1.1 安装&#xff1a;官网下载后&#xff0c;…

List常见方法和遍历操作

List集合的特点 有序&#xff1a; 存和取的元素顺序一致有索引&#xff1a;可以通过索引操作元素可重复&#xff1a;存储的元素可以重复 List集合的特有方法 Collection的方法List都继承了List集合因为有索引&#xff0c;所以有了很多操作索引的方法 ublic static void main…

鸿蒙HarmonyOS-图表应用

简介 随着移动应用的不断发展&#xff0c;数据可视化成为提高用户体验和数据交流的重要手段之一。在HarmonyOS应用开发中&#xff0c;一个强大而灵活的图表库是实现这一目标的关键。而MPChart就是这样一款图表库&#xff0c;它为开发者提供了丰富的功能和灵活性&#xff0c;使得…

linux实用技巧:ubuntu18.04安装samba服务器实现局域网文件共享

Ubuntu安装配置Samba服务与Win10共享文件 Chapter1 Ubuntu18.04安装配置Samba服务与Win10共享文件一、什么是Samba二、安装Samba1、查看是否有安装samba2、安装samba 三、配置Samba服务1、创建共享目录&#xff08;以samba_workspaces为例&#xff09;2、为samba设置登录用户3、…