它什么是?



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

为什么要用?



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

有什么区别?

下面以访问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;
}

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

  1. 基于C++11的数据库连接池实现

    0.注意 该篇文章为了让大家尽快看到效果,代码放置比较靠前,看代码前务必看下第4部分的基础知识. 1.数据库连接池 1.1 是什么? 数据库连接池负责分配.管理和释放数据库连接,属于池化机制的一种,类 ...

  2. JDBC(11)—数据库连接池

    在实际开发过程中,特别是在web应用系统中,如果程序直接访问数据库中的数据,每一次数据访问请求丢必须经历建立数据库连接.打开数据库.存取数据和关闭数据库连接.而连接并打开数据库是一件既消费资源又费时的 ...

  3. java web学习总结(十六) -------------------数据库连接池

    一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...

  4. JDBC 数据库连接池 小结

    原文:http://www.cnblogs.com/lihuiyy/archive/2012/02/14/2351768.html 当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用 ...

  5. 数据库连接池之Proxool使用

    如果想要搭建一个高效的网站,链接池是必须用到的一部分.而连接池的选择是多种多样的.就现在的软件开发界而言,最为多用的是DBCP, c3p0, 和 proxool.而hibernate推荐使用的是c3p ...

  6. c3p0数据库连接池死锁问题

    项目进行压力测试的时候,运行大概1小时候,后台抛出以下异常: Nov 9, 2012 1:41:59 AM com.mchange.v2.async.ThreadPoolAsynchronousRun ...

  7. paip.提升性能----数据库连接池以及线程池以及对象池

    paip.提升性能----数据库连接池以及线程池以及对象池 目录:数据库连接池c3po,线程池ExecutorService:Jakartacommons-pool对象池 作者Attilax  艾龙, ...

  8. java数据库连接池性能对比

    这个测试的目的是验证当前常用数据库连接池的性能. testcase Connection conn = dataSource.getConnection(); PreparedStatement st ...

  9. MVC设计模式((javaWEB)在数据库连接池下,实现对数据库中的数据增删改查操作)

    设计功能的实现: ----没有业务层,直接由Servlet调用DAO,所以也没有事务操作,所以从DAO中直接获取connection对象 ----采用MVC设计模式 ----采用到的技术 .MVC设计 ...

随机推荐

  1. 渗透测试之sql注入验证安全与攻击性能

    由于渗透测试牵涉到安全性以及攻击性,为了便于交流分享,本人这里不进行具体网址的透露了. 我们可以在网上查找一些公司官方网站如(http://www.XXXXXX.com/xxxx?id=1) 1.拿到 ...

  2. JavaScript之parseInt()方法

    parseInt(string, radix):用于解析一个字符串并返回指定基数的十进制整数或者NaN string参数为被解析的值,如果该值不是一个字符串,则会隐式的使用toString()方法转化 ...

  3. 六、LVM和从磁盘配额

    一.LVM概述 Logical Volume Manager,逻辑卷管理 优点:能够保证在现有数据不变的情况下,动态调整磁盘容量,从而提高磁盘管理的灵活性 /boot分区用于存放引导文件,不能基于LV ...

  4. UiPath鼠标操作图像的介绍和使用

    一.鼠标(mouse)操作的介绍 模拟用户使用鼠标操作的一种行为,例如单击,双击,悬浮.根据作用对象的不同我们可以分为对元素的操作.对文本的操作和对图像的操作 二.鼠标对图像的操作在UiPath中的使 ...

  5. 如何通过WinDbg获取方法参数值

    引入 我们在调试的过程中,经常会通过查看方法的输入与输出来确定这个方法是否异常.那么我们要怎么通过 WinDbg 来获取方法的参数值呢? WinDbg 中主要包含三种命令:标准命令.元命令(以 . 开 ...

  6. 网络通讯之Socket-Tcp(一)

    网络通讯之Socket-Tcp  分成3部分讲解: 网络通讯之Socket-Tcp(一): 1.如何理解Socket 2.Socket通信重要函数 网络通讯之Socket-Tcp(二): 1.简单So ...

  7. 用面向对象的方式操作 JSON 甚至还能做四则运算 JSON 库

    前言 在之前实现的 JSON 解析器中当时只实现了将一个 JSON 字符串转换为一个 JSONObject,并没有将其映射为一个具体的 struct:如果想要获取值就需要先做断言将其转换为 map 或 ...

  8. 禁用Chrome自动更新

    删除下Update目录 C:\Program Files (x86)\Google\Chrome\

  9. 09 MySQL_SQL日期函数和聚合函数

    日期相关的函数 seclect 'helloworld'; 1. 获取当前时间 now(); select now(); 2.获取当前的日期 curdate(); select curdate(); ...

  10. 【干货】MySQL底层架构设计,你了解多少?

    很多开发同学对SQL优化如数家珍,却对MySQL架构一知半解.岂不是只见树叶,不见森林,终将陷入细节中不能自拔. 今天就一块学习MySQL分层架构,深入了解MySQL底层实现原理,以及每层的作用,我们 ...