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的ip
    uint16_t port_;              // mysql的port
    std::string username_;       // 登录mysql的用户名 
    std::string password_;       // 登录mysql的密码
    std::string database_;       // 要访问的数据库名
    uint32_t initSize_;          // 初始连接数
    uint32_t maxSize_;           // 最大连接数
    uint16_t maxIdleTime_;       // 最大空闲时间 s
    int connectTimeout_;         // 获取连接的超时时间 ms

    std::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::bind
    produce.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.mfbz.cn/a/604180.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

06-beanFactoryPostProcessor的执行

文章目录 invokeBeanFactoryPostProcessors(beanFactory)invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);invokeBeanFactoryPostProcessors(regularPostProc…

JAVA基础之jsp标准标签

jsp动作标签实现实例化一个实体类 <jsp:useBean id"标识符" class"java类名" scope"作用范围"> 传统的java方式实例化一个实体类 Users user new Users(); <%%> id: 对象名 * class:类 创建对象时,完全限定名(包名…

设置默认表空间和重命名

目录 设置默认表空间 创建的临时表空间 tspace4 修改为默认临时表空间 创建的永久性表空间 tspace3 修改为默认永久表空间 重命名表空间 将表空间 tspace3 修改为 tspace3_1 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/13520…

Spring Boot | Spring Boot 整合 “RabbitMQ“ ( 消息中间件 ) 实现

目录: Spring Boot 整合 "RabbitMQ" ( 消息中间件 )实现 &#xff1a;一、Spring Boot 整合 整合实现 : Publish/Subscribe ( 发布订阅 ) 工作模式 ( "3种"整合实现方式 )1.1 基于"API"的方式 ( 实现 Publish/Subscribe "发布订阅"工作…

OSPF Stub区域

原理概述 OSPF 协议定义了多种区域&#xff08; Area &#xff09;类型&#xff0c;其中比较常见的有 Stub 区域和 Totally Stub 区域。区域的类型决定了在这个区域当中所存在的 LSA 的类型。 Stub 区域不允许 Type-4和 Type-5 LSA 进入&#xff0c;该区域会通过 Type-3 LSA…

电子商务对应的职业有哪些?10年互联网人透底行业秘密!

电子商务对应的职业有哪些&#xff1f;10年互联网人透底行业秘密&#xff01; 事实说话&#xff0c;实事求是&#xff0c;不要再把美颜滤镜下的市场&#xff0c;传给新人小伙伴了&#xff01; 大家好&#xff0c;我是微三云胡佳东&#xff0c;一家软件公司负责人&#xff01; …

keystone学习小结

1 keystone middleware 1.1 工作流程 middleware在客户端和服务端之间&#xff0c;会拦截客户端请求并判断请求身份是否是正确合法的&#xff0c;若是&#xff0c;则继续将请求发给其他middleware或app 具体看&#xff0c;干了这些事 1将请求里的auth header去除&#xff0c…

景源畅信:想要做抖音电商有哪些适合的发展渠道?

在数字浪潮的推动下&#xff0c;抖音电商如同一股不可阻挡的潮流&#xff0c;正吸引着无数创业者和品牌的目光。如何在这一领域获得成功&#xff0c;选择合适的发展渠道成为关键。接下来&#xff0c;让我们深入探讨这一话题&#xff0c;揭开抖音电商成功之路的秘密。 一、内容创…

C# Web控件与数据感应之 TreeView 类

目录 关于 TreeView 一些区别 准备数据源 范例运行环境 一些实用方法 获取数据进行呈现 ​根据ID设置节点 获取所有结点的索引 小结 关于 TreeView 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;本文将继续介绍与…

Idea + maven 搭建 SSH (struts2 +hibernate5 + spring5) 环境

org.apache.struts struts2-core 2.3.35 org.apache.struts struts2-spring-plugin 2.3.35 org.apache.struts struts2-json-plugin 2.3.8 1.4 配置Java EE 坐标依赖 这里可以引入 servlet api&#xff0c;jstl 标签库等一系列工具 javax.servlet javax.servlet-api …

c语言实现贪吃蛇小游戏————附全代码!!!

目录 1.Win32 API 1.1控制台应用程序 1.2控制台的名称&#xff0c;控制台窗口大小 1.3设置控制台光标位置 COORD - 光标坐标 GetStdHandle - 获取句柄 SetConsoleCursorPosition - 设置光标位置 封装一个设置光标的函数 1.4设置控制台光标的属性 CONSOLE_CURSOR_INFO …

【第13章】spring-mvc之validator

文章目录 前言一、准备1. 引入库2. add.jsp3. show.jsp 二、代码部分1.实体类2. 控制器类3. 效果4. 展示 总结 前言 【第20章】spring-validator 虽然前面已经在spring介绍过&#xff0c;但是为了保证代码可用&#xff0c;还是会从头讲到尾&#xff0c;尽量把关键点列出来讲给…

微服务架构中的挑战及应对方式:Outbox 模式

使用 Outbox 模式保持微服务数据一致性 在一个由许多小型服务组成的系统中保持数据一致性是困难的&#xff0c;因为它们分散在各处。以下是一些常见问题以及如何处理它们的方法&#xff1a;当服务发送消息时&#xff0c;同时更新数据库和发送消息是棘手的问题。 在微服务中发出…

【Qt 开发基础体系】Qt信号与槽机制

文章目录 1.Qt 信号与槽机制原理&#xff08;Signal & Slot&#xff09;2. QObject 类 connect 的介绍3. 信号与槽机制连接方式4. 信号和槽机制优势及其效率&#xff1a;3. 信号与槽机制应用 1.Qt 信号与槽机制原理&#xff08;Signal & Slot&#xff09; &#x1f42…

通过AOP实现项目中业务服务降级功能

最近项目中需要增强系统的可靠性&#xff0c;比如某远程服务宕机或者网络抖动引起服务不可用&#xff0c;需要从本地或者其它地方获取业务数据&#xff0c;保证业务的连续稳定性等等。这里简单记录下业务实现&#xff0c;主要我们项目中调用远程接口失败时&#xff0c;需要从本…

《武林秘籍》——闪侠惠递如何让消费者寄快递更安心!

现如今&#xff0c;网上下单寄快递的便利性让众多人享受到了电商物流飞速发展带来的红利性。今天小编直接介绍一款寄快递特别省钱的利器&#xff0c;就是利用闪侠惠递来寄快递。闪侠惠递寄快递&#xff0c;真正的实现了便宜寄快递发物流的便捷性&#xff0c;开创了低价发快递的…

【汇总】虚拟机网络不通(Xshell无法连接虚拟机)排查方法

搜索关键字关键字关键字&#xff1a;虚拟机虚拟机虚拟机连接失败、虚拟机无法连接、Xshell连接失败、ping baidu.com失败、静态IP设置 Kali、CentOS、远程连接 描述&#xff1a;物理机无法连接虚拟机&#xff1b;虚拟机无法访问百度&#xff0c;虚拟机无法访问baidu.com 虚拟机…

Logstash分析MySQL慢查询日志实践

删除匹配到的行&#xff0c;当前行信息不记录到message中

可视化面板布局适配屏幕-基于 flexible.js + rem 智能大屏适配

可视化面板布局适配屏幕-基于 flexible.js rem 智能大屏适配 VScode 安装cssrem插件引入flexible.js在之后的开发都使用rem为单位&#xff0c;安装cssrem插件就是为了快捷将px转为rem我们的设计稿是1920px&#xff0c;设置最小宽度为1024px&#xff0c;最后&#xff0c;我们可…

JavaScript异步编程——05-回调函数

我们在前面的文章《JavaScript 基础&#xff1a;异步编程/单线程和异步》中讲过&#xff0c;Javascript 是⼀⻔单线程语⾔。早期我们解决异步场景时&#xff0c;⼤部分情况都是通过回调函数来进⾏。 &#xff08;如果你还不了解单线程和异步的概念&#xff0c;可以先去回顾上一…