c++实现数据库连接池

介绍

为提高mysql的访问性能,可增加连接池。为什么他能提高性能: mysql是基于C/S架构,从客户端到服务器,一条sql的执行流程:tcp三次握手->mysql连接认证->执行sql->关闭mysql连接->tcp四次挥手 每次数据库都需要这5步太耗时,连接池缓存连接,后续直接用,5步变1步。

连接池原理

一般设置成单例,用一个队列存放所有的空闲连接。
组成部分:

1.  认证所需的信息
2. 初始连接数:创建单例时,构造函数就会创建初始连接数量的连接,以供使用
3. 最大连接数:存在的连接数不能超过它
4. 连接超时时间:当获取一条连接花费的时间超过此时间,则返回(如连接数达到最大了,又没有人释放)
5. 最大空闲时间:当连接在队列里存活的时间超过了此时间,且连接数>初始连接数,就删除多余连接直至初始连接数

代码示例,完整代码见https://github.com/1412771048/connect_pool:

#pragma once
#include <string>
#include <queue>
#include <mutex>
#include <atomic>
#include <thread>
#include <memory>
#include <condition_variable>
#include <functional>#include "mysql.hpp"
#include "SimpleIni.h"//连接池类
class ConnectPool {
public:ConnectPool(const ConnectPool&) = delete;ConnectPool(ConnectPool&&) = delete;ConnectPool& operator=(const ConnectPool&) = delete;ConnectPool& operator=(ConnectPool&&) = delete;~ConnectPool() = default;static ConnectPool& GetInstance();//给外部提供的接口:从连接池获取一条连接,且用智能指针管理,自定义删除器,使其析构时归还连接而不是释放连接std::unique_ptr<MySql, std::function<void(MySql*)>> GetConnection(); 
private:ConnectPool();               // 构造函数私有化,单例bool LoadConfig();           // 加载配置文件std::string ip_;             // mysql的ipuint16_t port_;              // mysql的portstd::string username_;       // 登录mysql的用户名 std::string password_;       // 登录mysql的密码std::string database_;       // 要访问的数据库名uint32_t initSize_;          // 初始连接数uint32_t maxSize_;           // 最大连接数uint16_t maxIdleTime_;       // 最大空闲时间 sint connectTimeout_;         // 获取连接的超时时间 msstd::queue<MySql*> connQue_;       // 存储空闲连接的队列std::mutex queueMtx_;              // 保护队列的互斥锁std::condition_variable cv_;       //条件变量std::atomic<uint16_t> connectCnt_; //记录连接的总数,且是线程安全的
};ConnectPool& ConnectPool::GetInstance() {static ConnectPool pool; //静态局部变量的初始化是线程安全的return pool;
}bool ConnectPool::LoadConfig() {CSimpleIniA ini;if (ini.LoadFile("mysql.conf") < 0) {return false;}ip_ = ini.GetValue("mysql", "ip");port_ = std::stoi(ini.GetValue("mysql", "port"));username_ = ini.GetValue("mysql", "username");password_ = ini.GetValue("mysql", "password");database_ = ini.GetValue("mysql", "database");initSize_ = std::stoi(ini.GetValue("mysql", "initSize"));maxSize_ = std::stoi(ini.GetValue("mysql", "maxSize"));maxIdleTime_ = std::stoi(ini.GetValue("mysql", "maxIdleTime"));connectTimeout_ = std::stoi(ini.GetValue("mysql", "connectTimeout"));return true;
}ConnectPool::ConnectPool() {if (!LoadConfig()) {return;}//连接池第一次时先创建初始数量的连接供外部使用for (int i = 0; i < initSize_; ++i) {MySql* p = new MySql;while (1) {if (p->connect(ip_, port_, username_, password_, database_)) {break;}}//出循环就一定连接上了connQue_.push(p);p->refreshAliveTime();++connectCnt_;}//创建一个生产者线程,等待连接不够请求再创建的请求std::thread produce([&](){while (1) {std::unique_lock<std::mutex> lock(queueMtx_);while (!connQue_.empty()) {//说明初始连接数都没用完cv_.wait(lock);}//被唤醒说明初始连接数用完且不够了,拿到锁开始生产if (connectCnt_ < maxSize_) {MySql* p = new MySql;while (1) {if (p->connect(ip_, port_, username_, password_, database_)) {break;}}//出循环就一定连接上了connQue_.push(p);p->refreshAliveTime();++connectCnt_;}//通知等待的消费者们cv_.notify_all();}});//函数较短就原地写,长就拆走,也可用std::bindproduce.detach();//开一个线程专门扫描超过最大空闲时间,进行连接回收std::thread scan([&](){while (1) {//用sleep模拟定时,每次睡一个最大空闲时间std::this_thread::sleep_for(std::chrono::seconds(maxIdleTime_));//可能会操作队列,要加锁std::unique_lock<std::mutex> lock(queueMtx_);while (connectCnt_ > initSize_) {//队首元素的存活时间最长,若它都没超过最大空闲时间,则可以不用判断了MySql* p = connQue_.front();if (p->GetAliveTime() < (maxIdleTime_ * 1000)) {break;} connQue_.pop();--connectCnt_;delete p;}}});scan.detach();
}std::unique_ptr<MySql, std::function<void(MySql*)>> ConnectPool::GetConnection() {std::unique_lock<std::mutex> lock(queueMtx_);if (connQue_.empty()) {//等待,时间若未被唤醒,也自动醒cv_.wait_for(lock, std::chrono::milliseconds(connectTimeout_));if (connQue_.empty()) {return nullptr; //获取连接超时}}//能走到这:要么队列不为空,要么队列为空被唤醒后不为空//自定义删除器,当客户端调用此函数获取连接,用完后,智能指针析构->归还连接std::unique_ptr<MySql, std::function<void(MySql*)>> sp(connQue_.front(), [&](MySql* p){std::unique_lock<std::mutex> lock(queueMtx_);connQue_.push(p);p->refreshAliveTime();});connQue_.pop();//消费后若队列空,则通知生产者if (connQue_.empty()) {cv_.notify_all();}return sp;
}

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

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

相关文章

jmeter控制器讲解

1&#xff0c;随机顺序控制器和随机控制器的区别&#xff1a;随机顺序控制器下所有的接口都会执行&#xff0c;只是执行顺序是随机的&#xff0c;随机控制器下所有的接口中随机执行一个接口&#xff0c;其余接口不执行。

【跨境商家必读】TikTok Shop商城运营全指南

随着社交媒体和电子商务之间界限的日益模糊&#xff0c;一种全新的购物平台——TikTok商城&#xff0c;正在迅速成为全球跨境商家们关注的焦点。在这个竞争激烈的TikTok跨境电商领域中&#xff0c;了解如何有效利用TikTok Shop的各项功能&#xff0c;理解其独特的运营模式&…

Linux进程——Linux进程间切换与命令行参数

前言&#xff1a;在上一篇了解完进程状态后&#xff0c;我们简单了解了进程优先级&#xff0c;然后遗留了一点内容&#xff0c;本篇我们就来研究进程间的切换&#xff0c;来理解上篇提到的并发。如果对进程优先级还有没理解的地方可以先阅读&#xff1a; Linux进程优先级 本篇…

Gmail邮箱怎么注册?2024年完整指南(包含跳过手机号验证)

一、为什么要注册Gmail邮箱&#xff1f; 全球通用性&#xff1a;Gmail是一个全球性的邮件服务平台&#xff0c;被广泛认可和信赖。因为客户对于Gmail的接受度高&#xff0c;无需担心邮件被自动标记为垃圾邮件。 整合营销工具&#xff1a;通过Gmail账号&#xff0c;你可以轻松…

拼接图片路径不显示:vue

下面这2中方法都不能显示图片 <img :src"/assets/ scope.row.iconName.svg" width"15"/> <img :src"/assets/icons/${scope.row.iconName}.svg" width"15"/> 而这样可以 <img src"/assets/icons/sign.svg" …

C++之大数运算

溪云初起日沉阁 山雨欲来风满楼 契子✨ 我们知道数据类型皆有范围&#xff0c;一旦超出了这个范围就会造成溢出问题 今天说说我们常见的数据类型范围&#xff1a; 我们平时写代码也会遇到数据类型范围溢出问题&#xff1a; 比如 ~ 我们之前写的学生管理系统在用 int类型 填写…

stack的使用

1.栈的定义 我们可以看到模板参数里面有一个容器适配器 &#xff0c;什么是适配器&#xff1f;比如充电器就叫做电源适配器&#xff0c;用在做转换&#xff0c;对电压进行相关的转换适配我们的设备。栈&#xff0c;队列不是自己直接管理数据&#xff0c;是让其他容器管理数据&a…

java-函数式编程-语法

目录 1、函数表现形式 分类 lambda表达式 参数类型可以全写&#xff0c;也可以全不写&#xff0c;但不能一部分写&#xff0c;一部分不写lambda 的省略策略&#xff1a;凡是可推导&#xff0c;都可以省略 方法引用 练习-判断语法正确性 练习-写出与方法引用等价的lambda表达式…

orpo 实例demo

代码参考地址 论文参考地址 demo import paddleclass LLm(paddle.nn.Layer):def __init__(self, voc, hidden):super(LLm, self).__init__()self.em paddle.nn.Embedding(voc, hidden)self.out_layer paddle.nn.Linear(hidden, voc, bias_attrFalse)def forward(self, x):…

新火种AI|马斯克聘用OpenAI泄密者,他们的梁子着实越结越深...

作者&#xff1a;小岩 编辑&#xff1a;彩云 就在最近&#xff0c;昔日就职于OpenAI的工程师Pavel Izmailov正式加入了马斯克的AI团队&#xff0c;他还在自己的推特上大张旗鼓的做着宣传&#xff1a;研究院xai。 AI工程师的跳槽本不值得惊讶&#xff0c;但Pavel的跳槽却在行…

LSTM神经网络 vs Transformer在量化中的应用

LSTM,全称Long Short-Term Memory,是一种特殊的递归神经网络。它通过巧妙的"门"结构,可以有效地捕捉时间序列数据中的长期依赖关系。这一特点,使得LSTM在处理股价这种具有时间序列特性的数据时,展现出了非凡的潜力。 这种特殊的递归神经网络 与一般的前馈神经网络不…

C++语法|如何写出高效的C++代码(一)|对象使用过程中背后调用了哪些方法(构造和析构过程)?

文章目录 再探拷贝构造函数和重载复制运算符实例化新对象和赋值操作强转为类类型指针和引用时临时对象的构造和析构过程 考考你问题答案 再探拷贝构造函数和重载复制运算符 实例化新对象和赋值操作 首先我们写一个类&#xff0c;实现它的拷贝构造并重载赋值运算符。 class T…

QSPI与SDIO差异

将STM32微控制器上的Quad SPI&#xff08;QSPI&#xff09;和Secure Digital Input/Output&#xff08;SDIO&#xff09;接口结合起来&#xff0c;可以显著增强嵌入式系统的功能&#xff0c;提供高速数据传输和非易失性存储选项。以下是如何在您的STM32项目中集成QSPI和SDIO的简…

盘点四种计算数组中元素值为1的个数的方法

目录 一、引言 二、方法一&#xff1a;基础循环遍历 三、方法二&#xff1a;列表推导式 四、方法三&#xff1a;使用内置函数sum和生成器表达式 五、方法四&#xff1a;使用NumPy库 六、性能比较 七、性能结果分析与讨论 八、最佳实践 九、总结 一、引言 在编程和数…

银河麒麟QT项目打包详细教程

银河麒麟QT项目打包详细教程 一、QT项目打包 下载linuxdeployqt&#xff0c;下载地址&#xff1a;https://github.com/probonopd/linuxdeployqt/releases 安装Linuxdeployqt 2.1 为了安装方便&#xff0c;将下载下来的文件名称改短些 mv linuxdeployqt-6-x86_64.AppImage lin…

Python中的bytearray对象,灵活的字节序列处理

写在前言 hello&#xff0c;大家好&#xff0c;我是一点&#xff0c;专注于Python编程&#xff0c;如果你也对感Python感兴趣&#xff0c;欢迎关注交流。 希望可以持续更新一些有意思的文章&#xff0c;如果觉得还不错&#xff0c;欢迎点赞关注&#xff0c;有啥想说的&#x…

92、动态规划-最小路径和

思路&#xff1a; 还是一样&#xff0c;先使用递归来接&#xff0c;无非是向右和向下&#xff0c;然后得到两种方式进行比较&#xff0c;代码如下&#xff1a; public int minPathSum(int[][] grid) {return calculate(grid, 0, 0);}private int calculate(int[][] grid, int …

射频无源器件之巴伦

一. 巴伦的作用 Balun,balanced-unbalanced的缩写,即平衡-不平衡转换器,是一种三端口无源器件,其本质是通过电感线圈的相互耦合实现的信号转换。用于平衡信号(差分信号,即振幅相等、相位相差180 的两个信号)与非平衡信号(单端信号)的相互转换。 Balun是高频电路重要器…

Scala 类和对象

类是对象的抽象&#xff0c;而对象是类的具体实例。类是抽象的&#xff0c;不占用内存&#xff0c;而对象是具体的&#xff0c;占用存储空间。类是用于创建对象的蓝图&#xff0c;它是一个定义包括在特定类型的对象中的方法和变量的软件模板。 我们可以使用 new 关键字来创建类…

Vmware虚拟机瘦身及Samba服务不可用问题解决

虚拟机磁盘空间膨胀是一个令人头疼的问题&#xff0c;特别是对许多搞开发的小伙伴。无论是做后台服务、嵌入式还是Android开发&#xff0c;都面临着这个难题。首先&#xff0c;操作系统本身就已占用不少空间&#xff0c;更新安装包&#xff0c;再下载一些开源软件&#xff0c;剩…