C++11实现的数据库连接池

2023-11-07

它什么是?

在这里插入图片描述
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;类似的还有线程池。

为什么要用?

在这里插入图片描述
一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。各种池化技术的使用原因都是类似的,也就是单独操作比较浪费系统资源,利用池提前准备一些资源,在需要时可以重复使用这些预先准备的资源,从而减少系统开销,实现资源重复利用。

有什么区别?

下面以访问MySQL为例,执行一个SQL命令,如果不使用连接池,需要经过哪些流程:

  • 建立通信连接的 TCP 三次握手
  • MySQL认证的三次握手
  • 真正的SQL执行
  • MySQL的关闭
  • 断开通信连接的 TCP 四次挥手

如果使用了连接池第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

Github

Connection.h
//
// Created by Cmf on 2022/8/24.
//

#ifndef CLUSTERCHATSERVER_CONNECTION_H
#define CLUSTERCHATSERVER_CONNECTION_H

#include <mysql/mysql.h>
#include <chrono>
#include <string>
#include "Log.hpp"

class Connection {
public:
    Connection();

    ~Connection();

    bool Connect(const std::string &ip, const uint16_t port, const std::string &user, const std::string &pwd,
                 const std::string &db);

    bool Update(const std::string &sql);

    MYSQL_RES *Query(const std::string &sql);

    void RefreshAliveTime();

    long long GetAliveTime() const;

private:
    MYSQL *_conn;
    std::chrono::time_point<std::chrono::steady_clock> _aliveTime;
};

#endif //CLUSTERCHATSERVER_CONNECTION_H
Connection.cpp
//
// Created by Cmf on 2022/8/24.
//

#include "Connection.h"

Connection::Connection() {
    _conn = mysql_init(nullptr);
    mysql_set_character_set(_conn, "utf8");//设置编码格式维utf8
}

Connection::~Connection() {
    if (_conn != nullptr) {
        mysql_close(_conn);
    }
}

bool Connection::Connect(const std::string &ip, const uint16_t port, const std::string &user, const std::string &pwd,
                         const std::string &db) {
    _conn = mysql_real_connect(_conn, ip.c_str(), user.c_str(), pwd.c_str(), db.c_str(), port, nullptr, 0);
    if (_conn == nullptr) {
        LOG_ERROR("MySQL Connect Error")
        return false;
    }
    return true;
}

bool Connection::Update(const std::string &sql) {
    if (mysql_query(_conn, sql.c_str()) != 0) {
        LOG_INFO("SQL %s 更新失败:%d", sql.c_str(), mysql_error(_conn));
        return false;
    }
    return true;
}

MYSQL_RES *Connection::Query(const std::string &sql) {
    if (mysql_query(_conn, sql.c_str()) != 0) {
        LOG_INFO("SQL %s 查询失败:%d", sql.c_str(), mysql_error(_conn));
        return nullptr;
    }
    return mysql_use_result(_conn);
}

void Connection::RefreshAliveTime() {
    _aliveTime = std::chrono::steady_clock::now();
}

long long Connection::GetAliveTime() const {
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _aliveTime).count();
}

ConnectionPool.h
//
// Created by Cmf on 2022/8/24.
//

#ifndef CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H
#define CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H

#include "noncopyable.hpp"
#include <memory>
#include <queue>
#include <mutex>
#include <atomic>
#include <thread>
#include <condition_variable>

#include "Connection.h"

class ConnectionPool : private noncopyable {
public:
    static ConnectionPool& GetConnectionPool(); //获取连接池对象实例
    //给外部提供接口,从连接池中获取一个可用的空闲连接
    std::shared_ptr<Connection> GetConnection();//智能指针自动管理连接的释放

    ~ConnectionPool();

private:
    ConnectionPool();

    bool LoadConfigFile();

    //运行在独立的线程中,专门负责生产新连接
    void ProduceConnectionTask();

    //扫描超过maxIdleTime时间的空闲连接,进行对于的连接回收
    void ScannerConnectionTask();

    void AddConnection();

private:
    std::string _ip;
    uint16_t _port;
    std::string _user;
    std::string _pwd;
    std::string _db;

    size_t _minSize;  //初始链接数量
    size_t _maxSize;   //最大连接数量
    size_t _maxIdleTime;//最大空闲时间
    size_t _connectionTimeout;//超时时间

    std::queue<Connection *> _connectionQueue;//存储连接队列
    std::mutex _mtx;    //维护连接队列的线程安全互斥锁
    std::atomic_int _connectionCount;//记录连接所创建的connection连接的总数量
    std::condition_variable _cv;//设置条件变量,用于连接生产线程和连接消费线程的通信
};

#endif //CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H
ConnectionPool.cpp
//
// Created by Cmf on 2022/8/24.
//
#include <fstream>
#include "ConnectionPool.h"
#include "json.hpp"

using json = nlohmann::json;

ConnectionPool &ConnectionPool::GetConnectionPool() {
    static ConnectionPool pool;
    return pool;
}

std::shared_ptr<Connection> ConnectionPool::GetConnection() {
    std::unique_lock<std::mutex> lock(_mtx);
    while (_connectionQueue.empty()) {  //连接为空,就阻塞等待_connectionTimeout时间,如果时间过了,还没唤醒
        if (std::cv_status::timeout == _cv.wait_for(lock, std::chrono::microseconds(_connectionTimeout))) {
            if (_connectionQueue.empty()) { //就可能还是为空
                continue;
            }
        }
    }
    //对于使用完成的连接,不能直接销毁该连接,而是需要将该连接归还给连接池的队列,供之后的其他消费者使用,于是我们使用智能指针,自定义其析构函数,完成放回的操作:
    std::shared_ptr<Connection> res(_connectionQueue.front(), [&](Connection *conn) {
        std::unique_lock<std::mutex> locker(_mtx);
        conn->RefreshAliveTime();
        _connectionQueue.push(conn);
    });
    _connectionQueue.pop();
    _cv.notify_all();
    return res;
}

ConnectionPool::ConnectionPool() {
    if (!LoadConfigFile()) {
        LOG_ERROR("JSON Config Error");
        return;
    }
    //创建初始数量的连接
    for (int i = 0; i < _minSize; ++i) {
        AddConnection();
    }
    //启动一个新的线程,作为连接的生产者 linux thread => pthread_create
    std::thread produce(std::bind(&ConnectionPool::ProduceConnectionTask, this));
    produce.detach();//守护线程,主线程结束了,这个线程就结束了
    //启动一个新的定时线程,扫描超过maxIdleTime时间的空闲连接,进行对于的连接回收
    std::thread scanner(std::bind(&ConnectionPool::ScannerConnectionTask, this));
    scanner.detach();
}

ConnectionPool::~ConnectionPool() {
    while (!_connectionQueue.empty()) {
        Connection *ptr = _connectionQueue.front();
        _connectionQueue.pop();
        delete ptr;
    }
}

bool ConnectionPool::LoadConfigFile() {
    std::ifstream ifs("../../config/dbconf.json");
    json js;
    ifs >> js;
    std::cout << js << std::endl;
    if (!js.is_object()) {
        LOG_ERROR("JSON is NOT Object");
        return false;
    }
    if (!js["ip"].is_string() ||
        !js["port"].is_number() ||
        !js["user"].is_string() ||
        !js["pwd"].is_string() ||
        !js["db"].is_string() ||
        !js["minSize"].is_number() ||
        !js["maxSize"].is_number() ||
        !js["maxIdleTime"].is_number() ||
        !js["timeout"].is_number()) {
        LOG_ERROR("JSON The data type does not match");
        return false;
    }
    _ip = js["ip"].get<std::string>();
    _port = js["port"].get<uint16_t>();
    _user = js["user"].get<std::string>();
    _pwd = js["pwd"].get<std::string>();
    _db = js["db"].get<std::string>();
    _minSize = js["minSize"].get<size_t>();
    _maxSize = js["maxSize"].get<size_t>();
    _maxIdleTime = js["maxIdleTime"].get<size_t>();
    _connectionTimeout = js["timeout"].get<size_t>();
    return true;
}

void ConnectionPool::ProduceConnectionTask() {
    while (true) {
        std::unique_lock<std::mutex> lock(_mtx);
        while (_connectionQueue.size() >= _minSize) {
            _cv.wait(lock);
        }
        if (_connectionCount < _maxSize) {
            AddConnection();
        }
        _cv.notify_all();
    }
}

void ConnectionPool::ScannerConnectionTask() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));
        std::unique_lock<std::mutex> lock(_mtx);
        while (_connectionCount > _minSize) {
            Connection *ptr = _connectionQueue.front();//队头的时间没超过,那后面的时间就都没超过
            if (ptr->GetAliveTime() >= _maxIdleTime * 1000) {
                _connectionQueue.pop();
                --_connectionCount;
                delete ptr;
            } else {
                break;
            }
        }
    }
}

void ConnectionPool::AddConnection() {
    Connection *conn = new Connection();
    conn->Connect(_ip, _port, _user, _pwd, _db);
    conn->RefreshAliveTime();
    _connectionQueue.push(conn);
    ++_connectionCount;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11实现的数据库连接池 的相关文章

  • 打击垃圾邮件机器人

    我的网站中有 C 表单 希望防止垃圾邮件机器人填写它 诀窍是 我想避免 CAPTHA 或任何其他用户输入 以避免丢失单个注册 以下是我心中的一些技巧 隐藏输入栏 问题 这还有效吗 跟踪时间 从第一个用户输入 关注名字 到发布表单 人类需要
  • C++ 返回值、引用、const 引用

    你能向我解释一下返回值 值引用和值常量引用之间的区别吗 Value Vector2D operator const Vector2D vector this gt x vector x this gt y vector y return t
  • 使用 C++ 拆分“[常规设置]”格式的节字符串

    我是 C 新手 我想读取包含部分和键值对的 ini 文件 根据部分 我想读取相应键的值 首先 我想阅读方括号内的部分 请帮忙 谢谢 对于真正的 INI 文件解析 我强烈建议iniparser库 http ndevilla free fr i
  • 如何在线程创建和退出时调用函数?

    include
  • 如何启动异步任务对象

    我想开始收集Task同时处理对象并等待所有对象完成 下面的代码显示了我想要的行为 public class Program class TaskTest private Task createPauseTask int ms works w
  • 将 Python 控制台集成到 GUI C++ 应用程序中

    I m going to add a python console widget into a C GUI below some other controls 许多类将暴露给 python 代码 包括一些对 GUI 的访问 也许我会考虑 P
  • Confuser .NET 混淆器。安全吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我目前正在开发一个应用程序 其中阻止用户反编译代码非常重要 现在 我意识到 如果由经验丰富的程序员执行 大多数 exe 都是可反编译的
  • 在 C# 中调用事件处理程序

    我一直在尝试学习如何在 C 中使用事件处理程序 但我无法弄清楚 handler this e 在以下代码中的作用 public event EventHandler ThresholdReached protected virtual vo
  • 使用 size_t 值反向遍历向量

    我想以相反的方向遍历向量的值 如您所知 向量的大小为 size t 当我使用以下代码时 for size t r m size 1 r gt 0 r x r f r for size t c r 1 c lt m size c x r m
  • WinForms - 表单大小错误

    我们有以下代码 private void MainForm Shown object sender EventArgs e RepositionForm private void RepositionForm Rectangle rect
  • 本地主机上的 .net HTTP_X_FORWARDED_FOR NULL

    抱歉 如果其他地方已经回答了这个问题 我找不到它 如果没有 我会尝试查找访问过该站点的机器的原始 IP 根据我的基本理解 变量HTTP X FORWARDED FOR无论代理和其他过滤器如何 都会显示用户的 IP 如果这是真的 我正在尝试对
  • C++ 错误:从“char”到“const char*”的转换无效

    我对 C 完全陌生 我创建了这个函数 bool guessWord string compWord cout lt lt Guess a letter string userLetter cin gt gt userLetter for u
  • 使用 QGraphicsScene 实现流畅的动画

    我希望我的问题并不总是同样的问题 我有一个 QGraphicsScene 它的项目是一些 QGraphicsPixmap 我用一个计时器来移动它们 每秒 SetX 10 我设置 10是因为窗口大100 使用这个解决方案我的动画不流畅 我想我
  • 按值返回的函数的返回语句中的初始化

    我的问题源于深入研究std move in return语句 例如以下示例 struct A A std cout lt lt Constructed lt lt this lt lt std endl A A noexcept std c
  • C# Julian 日期解析器

    我在电子表格中有一个单元格 它是 Excel 中的日期对象 但当它来自 C1 的 xls 类时 它会变成双精度型 类似于 2009 年 1 月 7 日的 39820 0 我读到这是儒略日期格式 有人可以告诉我如何在 C 中将其解析回 Dat
  • 字符串 c 的二叉树

    我正在尝试实现一个能够在 c 中保存字符串的二叉树 在让代码适用于整数之后 我尝试稍微修改它以处理字符数组 现在我似乎完全破解了代码 但不知道如何破解 任何帮助表示赞赏 include
  • 为什么调试器只显示数组指针中的一个元素?

    首先 我知道new是执行此操作的 C 方法 我只是表明有不止一种方法可以重现此错误 而且两种方法都令人难以置信的令人沮丧 我有两种形式的源文件 我正在尝试调试另一个编程作业 但我并没有寻求帮助 基本上 我正在尝试重新实施set作为一个类 具
  • 如何从尖点库矩阵格式获取原始指针

    我需要从尖点库矩阵格式获取原始指针 例如 cusp coo matrix
  • C# amo 获取角色完整

    我正在开发一个 SSAS 项目 其中除其他事项外 我需要获取 C 中表格多维数据集的完整用户列表 目前我让它以这样的方式工作 我可以获得角色 但数据不完整 当我调用 Server Database Roles 为了便于阅读而简化 属性并枚举
  • 如何在用户空间程序中使用内核 libcrc32c (或相同的函数)?

    我想在我自己的用户空间程序中进行一些 CRC 检查 我发现内核加密库已经在系统中 并且支持 SSE4 2 我尝试直接 include

随机推荐

  • C++ int,long和long long

    C 的整型有short int long和long long short为两字节存储 即16位 int的定义为存储位数大于等于short 也就是至少两个字节 long的定义为存储位数大于等于int long long的定义为存储位数大于等于
  • 31 KVM管理系统资源-管理虚拟内存NUMA

    文章目录 31 KVM管理系统资源 管理虚拟内存NUMA 31 1 NUMA简介 31 2 配置Host NUMA 操作步骤 31 3 配置Guest NUMA 操作步骤 31 KVM管理系统资源 管理虚拟内存NUMA 31 1 NUMA简
  • 车票识别 OCR系统带GUI界面 完整代码数据 可直接运行

    项目结构 import argparse import codecs import logging import os import os path as osp import sys from guiocr import appname
  • spring知识巩固

    文章目录 前言 什么是spring Spring 特点 spring框架用到了哪些设计模式 Spring中有多少个模块 它们分别是什么 什么是AOP 谈谈你对Spring中AOP的理解 AOP应用场景 Spring AOP中名词的概念 Sp
  • 管理基础知识20

    波特五力模型 掌握 波特五力模型是迈克尔 波特 Michael Porter 于20世纪80年代初提出 他认为行业中存在着决定竞争规模和程度的五种力量 这五种力量综合起来影响着产业的吸引力以及现有企业的竞争战略决策 五种力量分别为同行业内现
  • K8S集群搭建笔记

    K8S集群搭建笔记 1 Master的创建及配置 1 1 环境准备 1 2 初始化主节点 1 2 1 修改主节点配置信息 1 2 2 初始化主节点 kubeadm init 1 2 3 配置kubectl 1 2 4 检查master配置是
  • 队列基础使用示例与通过队列实现线程通信

    目录 一 队列基础解释 二 ConcurrentLinkedDeque 并发非阻塞式队列 三 BlockingQueue 阻塞队列 ArrayBlockingQueue LinkedBlockingQueue PriorityBlockin
  • idea编译时不提示任何错误信息解决方案

    问题描述 idea中 实现某个接口不提示导包 不提示实现方法 甚至 随便输入任何信息都不报错提示 解决方法一 1 打开File gt Settings gt Build Execution Deployment gt Compiler 2
  • CGAL功能大纲

    CGAL功能大纲 Computational Geometry Algorithms Library CGAL 计算几何算法库 使用C 语言编写的 提供高效 可控的算法库 广泛应用于计算几何相关领域 如地理信息系统 计算机图形学 计算机辅助
  • sonarqube安装

    开发十年 就只剩下这套Java开发体系了 gt gt gt 1 下载 下载地址 https www sonarqube org 2 Linux安装 解压 sonarqube zip 进入sonarqube 7 1 bin linux x86
  • jpa简介

    一 JPA是什么 1 JPA简介 JPA是Java Persistence API的简称 中文名Java持久层API 是JDK 5 0注解或XML描述对象 关系表的映射关系 并将运行期的实体对象持久化到数据库中 2 JPA 提供商 Hibe
  • 求最大公约数和最小公倍数的方法

    一 求最大公约数 1 辗转相除法 最推荐 不用管a b谁大谁小 如果b大 经过一次循环 a b会交换位置 include
  • html和js的学习

    链接如下 html https www runoob com html html attributes html js https www runoob com js js tutorial html
  • Gradle系列

    gradle idea使用 Gradle的使用教程 https blog csdn net qq 22172133 article details 81513955 原文网址 https www jianshu com p 46e7a916
  • Android强大的原生调试工具adb的常用命令

    文章目录 ADB简介 常用命令 列出链接的设备 进入设备的shell环境 设备日志 安装应用程序 卸载应用程序 将本地文件复制到调试设备上 将设备上的文件拉取到本地 启动程序 强制停止程序运行 截图 屏幕录制 列出调试设备所有的应用的报名
  • 说说数据一致性有哪几种?

    分析 回答 一般来说 数据一致性模型可以分为强一致性和弱一致性 强一致性也叫做线性一致性 除此以外 所有其他的一致性都是弱一致性的特殊情况 弱一致性根据不同的业务场景 又可以分解为更细分的模型 不同一致性模型又有不同的应用场景 强一致性 当
  • ETest_Tester-装备外场试验综合测试仪

    1 产品简介 装备外场试验综合测试仪ETest Tester是用于各行业装备软件研发 测试部门或者质量管理部门外场装备测试的综合测试设备 该设备由硬件和软件两部分组成 硬件采用全封闭 无风扇铝合金结构 嵌入式X86架构主板 软件部分包括操作
  • 苦逼的是怎么又有东西没记住,但我们依然每天坚持一遍、一遍又一遍指导记住为止。

    期待的是可以检验自己学习的成功 苦逼的是怎么又有东西没记住 但我们依然每天坚持一遍 一遍又一遍指导记住为止 原本以为大家会把讲过的都记录下来 以便日后毕业复习 事实证明18岁的我还是太年轻 一切想象的太美好 三番五次督促整理到自己笔记上 可
  • python基础—图形开发

    python基础 图形开发 python图形界面开发 认识tkinter模块 窗体的基本设置方法 几何布局管理器 pack布局管理器 grid布局管理器 place布局管理器 使用tkinter设计计算器程序 Python事件处理 常用tk
  • C++11实现的数据库连接池

    它什么是 数据库连接池负责分配 管理和释放数据库连接 它允许应用程序重复使用一个现有的数据库连接 而不是再重新建立一个 类似的还有线程池 为什么要用 一个数据库连接对象均对应一个物理数据库连接 每次操作都打开一个物理连接 使用完都关闭连接