C++11实现的数据库连接池
它什么是?
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;类似的还有线程池。
为什么要用?
一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。各种池化技术的使用原因都是类似的,也就是单独操作比较浪费系统资源,利用池提前准备一些资源,在需要时可以重复使用这些预先准备的资源,从而减少系统开销,实现资源重复利用。
有什么区别?
下面以访问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实现的数据库连接池的更多相关文章
- 基于C++11的数据库连接池实现
0.注意 该篇文章为了让大家尽快看到效果,代码放置比较靠前,看代码前务必看下第4部分的基础知识. 1.数据库连接池 1.1 是什么? 数据库连接池负责分配.管理和释放数据库连接,属于池化机制的一种,类 ...
- JDBC(11)—数据库连接池
在实际开发过程中,特别是在web应用系统中,如果程序直接访问数据库中的数据,每一次数据访问请求丢必须经历建立数据库连接.打开数据库.存取数据和关闭数据库连接.而连接并打开数据库是一件既消费资源又费时的 ...
- java web学习总结(十六) -------------------数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- JDBC 数据库连接池 小结
原文:http://www.cnblogs.com/lihuiyy/archive/2012/02/14/2351768.html 当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用 ...
- 数据库连接池之Proxool使用
如果想要搭建一个高效的网站,链接池是必须用到的一部分.而连接池的选择是多种多样的.就现在的软件开发界而言,最为多用的是DBCP, c3p0, 和 proxool.而hibernate推荐使用的是c3p ...
- c3p0数据库连接池死锁问题
项目进行压力测试的时候,运行大概1小时候,后台抛出以下异常: Nov 9, 2012 1:41:59 AM com.mchange.v2.async.ThreadPoolAsynchronousRun ...
- paip.提升性能----数据库连接池以及线程池以及对象池
paip.提升性能----数据库连接池以及线程池以及对象池 目录:数据库连接池c3po,线程池ExecutorService:Jakartacommons-pool对象池 作者Attilax 艾龙, ...
- java数据库连接池性能对比
这个测试的目的是验证当前常用数据库连接池的性能. testcase Connection conn = dataSource.getConnection(); PreparedStatement st ...
- MVC设计模式((javaWEB)在数据库连接池下,实现对数据库中的数据增删改查操作)
设计功能的实现: ----没有业务层,直接由Servlet调用DAO,所以也没有事务操作,所以从DAO中直接获取connection对象 ----采用MVC设计模式 ----采用到的技术 .MVC设计 ...
随机推荐
- 使用 KubeKey 搭建 Kubernetes/KubeSphere 环境的"心路(累)历程"
目录 今天要干嘛? 在哪里干? 从哪里开始干? 快速开干! 解决依赖问题再继续干! 如何干翻重来? 连着 KubeSphere 一起干! 干不过,输了. 重整旗鼓,继续干! 再次重整旗鼓,继续干! 一 ...
- 内网 Ubuntu 20.04 搭建 docusaurus 项目(或前端项目)的环境(mobaxterm、tigervnc、nfs、node)
内网 Ubuntu 20.04 搭建 docusaurus 项目(或前端项目)的环境 背景 内网开发机是 win7,只能安装 node 14 以下,而 spug 的文档项目采用的是 Facebook ...
- SAP 查看在线用户
SM04 可查看服务器全部客户端(Client)的用户的在线状态,并可以结束指定用户的会话状态,也就是强制踢出用户.
- linux查询文件或者文件夹
查找目录:find /(查找范围) -name '查找关键字' -type d // 查找fastdfs_storage_data文件夹 find / -name fastdfs_storage_da ...
- C++ 练气期之指针所指何处
1. 指针 指针是一种C++数据类型,用来描述内存地址. 什么是内存地址? 内存中的每一个存储单元格都有自己的地址,地址是使用二进制进行编码.地址从形态上看是一个整型数据类型.但是,它的数据含义并不表 ...
- 数据孤岛下的新破局 Real Time DaaS:面向 AP+TP 业务的数据平台架构
从传统数仓,到大数据平台,再到数据中台和湖仓一体新数据平台,在日益加重的数据孤岛困扰下,面向AP场景的解决方案可谓浩如烟海.但实际上,企业在TP类型业务上的投入和AP的比率却高达9:1,为什么没有为T ...
- 最著名的著名的比特币BTC钱包地址-中本聪的钱包
最著名的著名的比特币BTC钱包地址-中本聪的钱包1.比特币创始人中本聪 1PTFYUG6nCzRrByoRfGT5kefUNuZjNF84o这个地址还是比特币的创世地址,比特币从未移动过,其中的50币 ...
- 迭代器的实现原理和增强for循环
Iterator遍历集合--工作原理 在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位, 指向第 ...
- java,捕获和抛出异常
package Exrro; public class Test { //ctrl + alt + T快速生成异常捕捉 public static void main(String[] args) { ...
- HTTP协议之Expect爬坑
前言 今天,在对接一个第三方平台开放接口时遇到一个很棘手的问题,根据接口文档组装好报文,使用HttpClient发起POST请求时一直超时,对方服务器一直不给任何响应. 发起请求的代码如下: usin ...