[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线程 连接池
测试结果 和预期一样 多线程下使用连接池中的连接 比重复建立连接快很多

[mysql 基于C++实现数据库连接池 连接池的使用] 持续更新中
目背景 常见的MySQL、Oracle、SQLServer等数据库都是基于C/S架构设计的,即(客户端/服务器)架构,也就是说我们对数据库的操作相当于一个客户端,这个客户端使用既定的API把SQL语句通过网络发送给服务器端,MyS…...
【Flink SQL API体验数据湖格式之paimon】
前言 随着大数据技术的普及,数据仓库的部署方式也在发生着改变,之前在部署数据仓库项目时,首先想到的是选择国外哪家公司的产品,比如:数据存储会从Oracle、SqlServer中或者Mysql中选择,ETL工具会从Informa…...

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

Unity坦克大战开发全流程——开始场景——开始界面
开始场景——开始界面 step1:设置UI 反正按照这张图拼就行了 step2:写脚本 前面的拼UI都是些比较机械化的工作,直到这里写代码的时候才真正开始有点意思了,从这里开始,我们就要利用面向对象的思路来进行分析࿱…...

【SpringCloud】从实际业务问题出发去分析Eureka-Server端源码
文章目录 前言1.EnableEurekaServer2.初始化缓存3.jersey应用程序构建3.1注册jeseryFilter3.2构建JerseyApplication 4.处理注册请求5.registry() 前言 前段时间遇到了一个业务问题就是k8s滚动发布Eureka微服务的过程中接口会有很多告警,当时…...

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

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

2023年高级软考系统架构师考题参考
对于一些有实践经验的同学来说,感觉不难,但是落笔到纸面上,就差强人意了,平时这方面要多练习,所想所思要落到纸面上,或者表达清晰让别人听懂,不仅是工作中的一个基本素质,也是个非常…...

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

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

Node.js-fs、path、http模块
1.初识Node.js 1.1 什么是Node.js 1.2 Node.js中的JavaScript运行环境 1.3 Node.js可以做什么 Node.js 作为一个JavaScript 的运行环境,仅仅提供了基础的功能和 AP1。然而,基于 ode.s 提供的这些基础能,很多强大的工具和框架如雨后春笋&…...
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别名,查看长格式详细信息 alias ssls -l 2.创建ss别名,复制boot文件夹下的内容到data文件夹下 alias sscp -r /boot /data 3.删除别名ss unalias ss 4. 复制test文件夹下的passwd文件到qq文件夹下,并改名为ww cp test/pas…...

杰发科技AC7840——EEPROM初探
0.序 7840和7801的模拟EEPROM使用不太一样 1.现象 按照官方Demo,在这样的配置下,我们看到存储是这样的(连续三个数字1 2 3)。 使用串口工具的多帧发送功能 看不出多少规律 修改代码后 发现如下规律: 前四个字节是…...
WPF 基础入门(简介)
简介 WPF(Windows Presentation Foundation)是微软推出的基于Windows 的用户界面框架,属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了…...

【Unity动画系统】Animator有限状态机参数详解
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...

php获取访客IP、UA、操作系统、浏览器等信息
最近有个需求就是获取下本地的ip地址、网上搜索了相关的教程,总结一下分享给大家、有需要的小伙伴可以参考一下 一、简单的获取 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
语言:Python 框架:django/flask可以定制 软件版本:python3.7.7 数据库:mysql 数据库工具:Navicat 开发工具pycharm/vscode都可以 前端框架:vue.js 系统使用过程主要涉及到管理员和用户两种角色,主要包含个…...
面试题-性能优化
前端项目优化: 一般考虑方面: (挑几点记住) 我们学的: 懒加载: 路由、图片懒加载 骨架屏的使用 压缩文件:可以使用压缩工具(如GZIP)对页面文件进行压缩,减小文件大小,提高页面加载速度。 减少HTTP请求&a…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
React核心概念:State是什么?如何用useState管理组件自己的数据?
系列回顾: 在上一篇《React入门第一步》中,我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目,并修改了App.jsx组件,让页面显示出我们想要的文字。但是,那个页面是“死”的,它只是静态…...