c++实现数据库连接池

embedded/2024/9/25 21:25:13/

介绍

为提高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.ppmy.cn/embedded/40288.html

相关文章

使用函数波动性优化 PostgreSQL 查询:Volatile, Stable, and Immutable

文章目录 1.overview2. Volatile函数3. Stable函数4. Immutable 函数结论 1.overview 在 PostgreSQL 中&#xff0c;函数的不同波动性选择——即 Volatile&#xff08;易变&#xff09;、Stable&#xff08;稳定&#xff09;和Immutable&#xff08;不可变&#xff09;——在查…

【玩转Google云】GCP 制品管理:Artifact Registry 使用详解

本篇博文将带您深入了解 Google Cloud Platform (GCP) 的 Artifact Registry,一个功能强大的统一制品仓库,用于存储、管理和保护您的软件制品。我们将详细介绍 Artifact Registry 的核心概念、优势以及使用步骤,帮助您轻松上手并将其集成到您的开发流程中。 目录 一、Arti…

Kubernetes最小单元Pod的生命周期

1.1 Pod生命周期 1.1.1 过程及状态 Pod 的生命周期管理是 Kubernetes 集群中非常重要的一部分&#xff0c;它涉及到 Pod 从创建到销毁的整个过程。下面是 Pod 生命周期中各个阶段的简要说明&#xff1a; Pod 创建过程&#xff1a;当一个 Pod 被创建时&#xff0c;Kubernetes 会…

【Go语言初探】(一)、Linux开发环境建立

一、操作系统选择 选择在Windows 11主机上运行的CentOS 7 Linux 虚拟机&#xff0c;虚拟化平台为VMWare Workstation. 二、安装Go语言环境 访问Go语言官网&#xff0c;选择Linux版本下载&#xff1a; 解压&#xff1a; tar -xvf go1.22.3.linux-amd64.tar.gz检验安装结果&…

[单机]完美国际_V155_GM工具_VM虚拟机

[端游] 完美国际单机版V155一键端PC电脑网络游戏完美世界幻海凌云家园 本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是…

香港虚拟主机哪里可以试用?用于企业建站的

香港虚拟主机适合个人、企业建站&#xff0c;包括外贸企业网站、个人博客网站、中小企业官网等&#xff0c;那么作为新手不知道哪家香港虚拟主机好用的时候&#xff0c;该如何找到可以试用的香港虚拟主机呢&#xff1f; 香港虚拟主机也称作香港空间、香港虚拟空间&#xff0c;…

C++ | Leetcode C++题解之第76题最小覆盖子串

题目&#xff1a; 题解&#xff1a; class Solution { public:unordered_map <char, int> ori, cnt;bool check() {for (const auto &p: ori) {if (cnt[p.first] < p.second) {return false;}}return true;}string minWindow(string s, string t) {for (const au…