【C++】基于C++11的数据库连接池

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在进行数据库操作的时候为了提高数据库(关系型数据库)的访问瓶颈,除了在服务器端增加缓存服务器(例如redis)缓存常用的数据之外,还可以增加连接池,来提高数据库服务器的访问效率。

一般来说,对于数据库操作都是在访问数据库的时候创建连接,访问完毕断开连接。但是如果在高并发情况下,有些需要频繁处理的操作就会消耗很多的资源和时间,比如:

  • 建立通信连接的TCP三次握手
  • 数据库服务器的连接认证
  • 数据库服务器关闭连接时的资源回收
  • 断开通信连接的TCP四次挥手

如果使用数据库连接池会减少这一部分的性能损耗

编写代码需要的头文件:
#include<mysql.h>
#include<json/json.h>
#include<jsoncpp.h>

API对应的MySQL动态库
Windows:libmysql.dll
Linux:libmysqlclient.os


一、连接池设计

1.1 功能设计

要设计一个数据库连接池,我们需要实现以下几个功能点:

  1. 连接池只需要一个实例,所以连接池类应该是一个单例模式的类 (此为设计模式)
  2. 所有的数据库连接应该维护到一个安全的队列中
    • 使用队列的目的是方便连接的添加和删除
    • 安全指的是线程安全,也就是说需要使用互斥锁来保护队列数据的读写。
  3. 在需要的时候可以从连接池中得到一个或多个可用的数据库连接
    • 如果有可用连接,直接取出
    • 如果没有可用连接,阻塞等待一定时长然后再重试
    • 如果队列中没有多余的可用连接,需要动态的创建新连接
    • 如果队列中空闲的连接太多,需要动态的销毁一部分
  4. 数据库操作完毕,需要将连接归还到连接池中
    在这里插入图片描述

1.2 封装设计

  • 数据库连接的存储:可用使用STL中的队列queue
  • 连接池连接的动态创建:这部分工作需要交给一个单独的线程来处理
  • 连接池连接的动态销毁:这部分工作需要交给一个单独的线程来处理
  • 数据库连接的添加和归还:这是一个典型的生产者和消费者模型
    • 消费者:需要访问数据库的线程,数据库连接被取出(消费)
    • 生产者:专门负责创建数据库连接的线程
    • 处理生产者和消费者模型需要使用条件变量阻塞线程
  • 连接池的默认连接数量:连接池中提供的可用连接的最小数量
    • 如果不够就动态创建
    • 如果太多就动态销毁
  • 连接池的最大连接数量:能够创建的最大有效数据库连接上限
  • 最大空闲时间:创建出的数据库连接在指定时间长度内一直未被使用,此时就需要销毁该连接。
  • 连接超时:消费者线程无法获取到可用连接是,阻塞等待的时间长度

综上所述,数据库连接池对应的单例模式的类的设计如下:

using namespace std;
/*
* 数据库连接池: 单例模式
* MySqlConn 是一个连接MySQL数据库的类
*/
class ConnectionPool {
   
public:
    // 得到单例对象
    static ConnectionPool* getConnectPool();
    // 从连接池中取出一个连接
    shared_ptr<MySqlConn> getConnection();
    // 删除拷贝构造和拷贝赋值运算符重载函数
    ConnectionPool(const ConnectionPool& obj) = delete;
    ConnectionPool& operator=(const ConnectionPool& obj) = delete;

private:
    // 构造函数私有化
    ConnectionPool();
    bool parseJsonFile();
    void produceConnection();
    void recycleConnection();
    void addConnection();

    string m_ip;             // 数据库服务器ip地址
    string m_user;           // 数据库服务器用户名
    string m_dbName;         // 数据库服务器的数据库名
    string m_passwd;         // 数据库服务器密码
    unsigned short m_port;   // 数据库服务器绑定的端口
    int m_minSize;           // 连接池维护的最小连接数
    int m_maxSize;           // 连接池维护的最大连接数
    int m_maxIdleTime;       // 连接池中连接的最大空闲时长
    int m_timeout;           // 连接池获取连接的超时时长

    queue<MySqlConn*> m_connectionQ; // 连接队列
    mutex m_mutexQ;  // 互斥锁
    condition_variable m_cond; // 条件变量
};

二、连接数据库步骤

在程序中连接MySql服务器,主要分为已经几个步骤:

  1. 初始化连接环境
  2. 连接mysql的服务器,需要提供如下连接数据:
  • 服务器的IP地址 服务器监听的端口(默认端口是3306)
  • 连接服务器使用的用户名(默认是 root),和这个用户对应的密码
  • 要操作的数据库的名字
  1. 连接已经建立, 后续操作就是对数据库数据的添删查改

  2. 如果要进行数据 添加/ 删除/ 更新,需要进行事务的处理

    • 需要对执行的结果进行判断
      • 成功:提交事务
      • 失败:数据回滚
  3. 数据库的读操作 -> 查询 -> 得到结果集

  4. 遍历结果集 -> 得到了要查询的数据

  5. 释放资源

三、封装编码

3.1 VS配置

3.1.1 MySQL环境

打开项目的属性窗口,在属性页配置MySQL头文件目录和MySQL的库目录
在这里插入图片描述
将下载的mysql路径下的include和lib文件目录分别写入包含目录和库目录中
在这里插入图片描述
在VS项目的属性页窗口指定要加载的动态库对应的导入库(xxx.lib)
在这里插入图片描述
我们在调用MySQL API的使用需要加载的动态库为libmysql.dll,它对应的导入库为libmysql.lib,在该窗口的附加依赖项位置指定的就是这个导入库的名字。

3.1.2 jsoncpp环境

参考博客:jsoncpp的编译和使用

3.2 连接类代码设计

3.2.1 连接类MysqlConn.h

#pragma once
#include <string>
#include <WinSock2.h>//必须要有,解决“fd”:未知重写说明符
#include <algorithm>
#include<chrono>
#include<mysql.h>
using namespace std;
using namespace chrono;
class MysqlConn
{
   
public:
	// 初始化数据库连接
	MysqlConn();
	// 释放数据库连接
	~MysqlConn();
	// 连接数据库
	bool connect(string userName,string passwd,string dbName,string ip,unsigned short port=3306);
	// 更新数据库
	bool update(string sql);
	// 查询数据库
	bool query(string sql);
	// 遍历查询得到的结果集
	bool next();
	// 得到结果集中的字段值
	string value(int index);
	// 事务操作
	bool transaction();
	// 提交事务
	bool commit();
	// 事务回滚
	bool rollback();
	// 刷新起始的空闲时间点
	void refreshAliveTime();
	// 计算连接存活的时长
	long long getAliveTime();

private:
	void freeResource();  // 释放资源
	MYSQL* m_conn = nullptr; // 保存初始化数据库时返回的地址
	MYSQL_RES* m_result = nullptr; // 保存查询结果
	MYSQL_ROW m_row = nullptr; // 保存当前记录中每个字段的值
	steady_clock::time_point m_alivetime; // 存活时间
};

3.2.1 连接类MysqlConn.cpp

#include "MysqlConn.h"

 // 初始化数据库连接
MysqlConn::MysqlConn()
{
   
	// 初始化数据库
	this->m_conn = mysql_init(nullptr);
	// 设置接口的字符编码,防止在数据库操作时中文乱码
	mysql_set_character_set(this->m_conn, "utf8");
}


// 释放数据库连接
MysqlConn::~MysqlConn()
{
   
	// 释放内存
	if (this->m_conn != nullptr) {
   
		mysql_close(this->m_conn);
	}
	this->freeResource();
}


// 连接数据库
bool MysqlConn::connect(string userName, string passwd, string dbName, string ip, unsigned short port)
{
   
	// c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同。
	// 因为调用的MysqlAPI是C语言规范
	MYSQL* ptr = mysql_real_connect(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值