服务器编程:数据库连接池

引言:
数据库连接池和线程池的思想一样,是为了避免频繁创建和销毁数据库连接导致的性能开销。如果一个项目频繁的需要访问数据库,那么它就有可能需要频繁的创建/销毁数据库连接,那么我们可以采用数据库连接池的技术,在需要时,从数据库连接池中获取数据库连接,在用完数据库连接后再将它重新放回连接池中.

本文章所有代码大都来自开源项目:TinyWebServer

目录

  • 设计模式:单例模式
  • 数据库连接池
    • 头文件与基本函数
    • 连接池初始化
    • 获取数据库连接
    • 释放当前使用的连接
    • 销毁整个连接池
    • 源代码

设计模式:单例模式

单例模式就是指一个类有且只能有一个实例

实现单例模式的方法:
1.定义一个静态成员变量,用来保存唯一的一个实例
2.定义一个静态成员函数,用来让其他对象可以获取到这个实例
3.将构造函数设置为私有,防止其他类进行实例化对象

单例模式的好处:
1.避免多次实例化造成的性能和资源的开销
2.共享资源,由于只有一个实例,多个对象可以共享这个实例,实现资源共享和协作

小白对单例模式的误区:
单例模式是指该类只有一个实例,这个实例是保存在类中的静态成员变量中的,因为static关键字,所以所有的对象可以共享这个实例,并通过静态成员函数获取这个实例;但是这并不代表,单例模式只能创建一个对象,单例模式是可以创建多个对象的,然后这些对象通过静态成员函数获取实例将实例赋值给自己,实现相应的操作和共享数据等功能.

一个简单的单例模式的代码:

#include <iostream>class Singleton {
private:static Singleton* instance; // 单例实例// 私有构造函数,防止外部实例化Singleton() {}public:// 获取单例实例的静态方法static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}// 示例方法void showMessage() {std::cout << "Hello, I am a Singleton instance." << std::endl;}
};// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;int main() {Singleton* singleton1 = Singleton::getInstance();singleton1->showMessage();Singleton* singleton2 = Singleton::getInstance();singleton2->showMessage();// 判断两个实例是否相等if (singleton1 == singleton2) {std::cout << "Both instances are the same." << std::endl;} else {std::cout << "Instances are different." << std::endl;}return 0;
}

数据库连接池

头文件与基本函数

使用数据库所需要的头文件:

#include <mysql/mysql.h>

接下来将介绍几个跟数据库有关的函数:

1.mysql_init

int mysql_init(MYSQL *mysql)

用于初始化一个mysql对象

2.mysql_real_connect

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)

建立与mysql服务器的连接

参数1: MYSQL对象指针,用于接收连接句柄。
参数2: MySQL服务器主机名或IP地址。
参数3: MySQL用户名。
参数4: MySQL密码。
参数5: MySQL数据库名。
参数6: MySQL服务器端口号。
参数7: 额外的连接选项,可以为NULL。

3.mysql_close()

void mysql_close(MYSQL *mysql)

关闭与Mysql服务器的连接.

先只介绍这三个函数,因为我们的连接池只用这三个函数就可以实现,如果对其他函数感兴趣,可以去查找
头文件 <mysql/mysql.h>下的常用函数


TinyWebServer中
sql_connection_pool.h文件中的定义:

在这里插入图片描述
下面我会根据
TinywebServer项目中的
sql_connection_pool.cpp文件中对这些函数的具体实现做出分析来讲解数据库连接池

连接池初始化

void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log)
{m_url = url;m_Port = Port;m_User = User;m_PassWord = PassWord;m_DatabaseName = DBName;m_close_log = close_log;for (int i = 0; i < MaxConn; i++){MYSQL *con = NULL;con = mysql_init(con);//初始化一个mysql结构体对象!if (con == NULL){LOG_ERROR("MySQL Error");//日志记录错误!exit(1);}con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);if (con == NULL){LOG_ERROR("MySQL Error");exit(1);}connList.push_back(con);//将这个链接放入连接池队列中!++m_FreeConn;//空闲的链接数+1;}reserve = sem(m_FreeConn); //信号量 sem=sem(m_MaxConn = m_FreeConn;//最多连接数=当前所有空闲的连接数!
}

1.首先初始化自己的URL,端口,数据库用户名,数据库密码,数据库名,还有日志名等属性

2.然后根据最大的连接数,按照循环的方式,创建数据库连接,然后把数据库连接放入线程池,并更新信息:
2.1:先用mysql_init函数初始化一个mysql对象
2.2:用mysql_real_connect将mysql对象和数据库连接
2.3:将这个mysql对象添加到连接池队列中
2.4:更新基本信息:空闲的连接数+1

3.初始化一个信号量,信号量的初始值为数据库连接池中的连接数

4.更新基本信息,数据库连接池上限=当前空闲的连接数.

获取数据库连接

MYSQL *connection_pool::GetConnection()
{MYSQL *con = NULL;if (0 == connList.size())return NULL;reserve.wait();lock.lock();con = connList.front();connList.pop_front();--m_FreeConn;++m_CurConn;lock.unlock();return con;
}

获取数据库连接就是从连接池中取出一个连接然后使用,步骤如下:

1.首先创建一个MYSQL对象指针,并设置为空

2.判断连接池中是否有连接,如果没有,那就拿取失败

3.信号量wait操作,让信号量的值-1,意味着我要开始拿资源了,如果信号量的值为0的话,此线程会被阻塞住,从而无法继续申请互斥锁拿资源.

4.加上互斥锁,防止多个线程同时拿取资源导致错误

5.从连接池中取出第一个连接

6.更改基本信息,空闲的连接-1,当前使用的连接数+1

7.释放互斥锁,让其他线程也能申请互斥锁拿资源

释放当前使用的连接

bool connection_pool::ReleaseConnection(MYSQL *con)
{if (NULL == con)return false;lock.lock();connList.push_back(con);++m_FreeConn;--m_CurConn;lock.unlock();reserve.post();return true;
}

使用完一个数据库连接之后,应该再把这个连接放回连接池,步骤如下:

1.判断所要销毁的连接是否为空,如果为空,则直接退出

2.申请互斥锁,避免有其他的线程也进行释放操作

3.将这个连接放回连接池

4.更新基本信息:空闲连接+1,当前使用连接-1

5.将信号量的值+1,唤醒之前因为信号量所阻塞的线程,让他们可以继续申请互斥锁并获取连接.

销毁整个连接池

void connection_pool::DestroyPool()
{lock.lock();if (connList.size() > 0){list<MYSQL *>::iterator it;for (it = connList.begin(); it != connList.end(); ++it){MYSQL *con = *it;mysql_close(con);}m_CurConn = 0;m_FreeConn = 0;connList.clear();}lock.unlock();
}

将整个数据库连接池销毁,步骤如下:

1.加上互斥锁,避免多线程同时执行销毁操作

2.遍历整个连接池,使用mysql_close函数关闭连接池中的所有连接

3.更新基本信息:空闲连接=0,当前连接=0

4.清空整个连接池List

5.释放互斥锁

源代码

sql_connection_pool.cpp

#include <mysql/mysql.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <list>
#include <pthread.h>
#include <iostream>#include "sql_connection_pool.h"using namespace std;connection_pool::connection_pool()
{m_CurConn = 0;m_FreeConn = 0;
}connection_pool *connection_pool::GetInstance()
{static connection_pool connPool;return &connPool;
}//构造初始化
void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log)
{m_url = url;m_Port = Port;m_User = User;m_PassWord = PassWord;m_DatabaseName = DBName;m_close_log = close_log;for (int i = 0; i < MaxConn; i++){MYSQL *con = NULL;con = mysql_init(con);//初始化一个mysql结构体对象!if (con == NULL){LOG_ERROR("MySQL Error");//日志记录错误!exit(1);}con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);//mysql.h中用于建立实际数据库连接的函数。它的原型声明如下://if (con == NULL){LOG_ERROR("MySQL Error");exit(1);}connList.push_back(con);//将这个链接放入连接池队列中!++m_FreeConn;//空闲的链接数+1;}reserve = sem(m_FreeConn); //信号量 sem=sem(m_MaxConn = m_FreeConn;//最多连接数=当前所有空闲的连接数!
}//当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数
MYSQL *connection_pool::GetConnection()
{MYSQL *con = NULL;if (0 == connList.size())return NULL;reserve.wait();lock.lock();con = connList.front();connList.pop_front();--m_FreeConn;++m_CurConn;lock.unlock();return con;
}//释放当前使用的连接
bool connection_pool::ReleaseConnection(MYSQL *con)
{if (NULL == con)return false;lock.lock();connList.push_back(con);++m_FreeConn;--m_CurConn;lock.unlock();reserve.post();return true;
}//销毁数据库连接池
void connection_pool::DestroyPool()
{lock.lock();if (connList.size() > 0){list<MYSQL *>::iterator it;for (it = connList.begin(); it != connList.end(); ++it){MYSQL *con = *it;mysql_close(con);}m_CurConn = 0;m_FreeConn = 0;connList.clear();}lock.unlock();
}//当前空闲的连接数
int connection_pool::GetFreeConn()
{return this->m_FreeConn;
}connection_pool::~connection_pool()
{DestroyPool();
}connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){*SQL = connPool->GetConnection();conRAII = *SQL;poolRAII = connPool;
}connectionRAII::~connectionRAII(){poolRAII->ReleaseConnection(conRAII);
}

sql_connection_pool.h

#ifndef _CONNECTION_POOL_
#define _CONNECTION_POOL_#include <stdio.h>
#include <list>
#include <mysql/mysql.h>
#include <error.h>
#include <string.h>
#include <iostream>
#include <string>
#include "../lock/locker.h"
#include "../log/log.h"using namespace std;class connection_pool
{
public:MYSQL *GetConnection();				 //获取一个数据库连接bool ReleaseConnection(MYSQL *conn); //释放一个数据库连接int GetFreeConn();					 //获取空闲的连接数void DestroyPool();					 //销毁整个连接池//单例模式static connection_pool *GetInstance();void init(string url, string User, string PassWord, string DataBaseName, int Port, int MaxConn, int close_log);
private:connection_pool();~connection_pool();int m_MaxConn;  //最大连接数int m_CurConn;  //当前已使用的连接数int m_FreeConn; //当前空闲的连接数locker lock;   //定义了一个互斥锁list<MYSQL *> connList; //连接池sem reserve;   //定义了一个信号量,叫reserve.用来记录空闲的连接数public:string m_url;			 //主机地址string m_Port;		 //数据库端口号string m_User;		 //登陆数据库用户名string m_PassWord;	 //登陆数据库密码string m_DatabaseName; //使用数据库名int m_close_log;	//日志开关
};class connectionRAII{public:connectionRAII(MYSQL **con, connection_pool *connPool);~connectionRAII();private:MYSQL *conRAII;connection_pool *poolRAII;
};#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/1893.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【几何数学】【Python】【C++】将线段沿着线段方向延长一定长度,求新的点

p1点和p2点是一条线段的两端&#xff0c;沿着p1指向p2的方向&#xff0c;将线段长度延伸长度x&#xff0c;求延伸后的点ep。如下图&#xff1a; Python代码&#xff1a; import mathdef extend_line_segment(p1, p2, extension_length):"""延伸线段长度并返回…

Redis各数据类型操作命令

一、Redis数据类型及命令 &#xff08;一&#xff09;String 类别命令描述命令示例备注取/赋值操作赋值set key valueset lclkey lclvalue取值 get keyget lclkey取值并赋值getset key valuegetset lclkey1 lclvalue1获取原值&#xff0c;并设置新的值仅当不存在时赋值setnx k…

多元回归预测 | Matlab海洋捕食者算法(MPA)优化极限学习机ELM回归预测,MPA-ELM回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab海洋捕食者算法(MPA)优化极限学习机ELM回归预测,MPA-ELM回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %% …

Java 实现word、excel、ppt、txt等办公文件在线预览功能!

如何用 Java 实现word、excel、ppt、txt等办公文件在线预览功能&#xff1f;本文告诉你答案&#xff01; java 实现办公文件在线预览功能是一个大家在工作中也许会遇到的需求&#xff0c;网上些公司专门提供这样的服务&#xff0c;不过需要收费。 如果想要免费的&#xff0c;…

设计模式第19讲——命令模式(Command)

目录 一、什么是命令模式 二、角色组成 三、优缺点 四、应用场景 4.1 生活场景 4.2 java场景 五、代码实现 5.0 代码结构 5.1 抽象命令&#xff08;Command&#xff09;——Command 5.2 接收者&#xff08;Receiver&#xff09;——Chef 5.3 具体命令&#xff08;Co…

【读书笔记】《软件工程导论》

目录 一、软件工程概述 二、启动阶段 三、计划阶段 四、实施阶段 五、收尾阶段 一、软件工程概述 软件危机&#xff1a;在计算机软件的开发和维护过程中遇到的一系列严重问题。 软件危机的产生与自身的特点有关&#xff0c;还与软件开发、管理的方法不正确有关。 软件危…

Go语言并发微服务分布式高可用

Go语言并发微服务分布式高可用 Go语言基础 环境安装 命令行输入go&#xff0c;当前操作系统Os环境中依赖于PATH指定的日录们去找命令(可执行文件)windows会优先搜索当前日录&#xff0c;当前日录没有才依赖PATH中指定的日录 环境变量: 操作系统运行环境中提前定义好的变量P…

6-js基础-3

JavaScript 基础 - 3 知道什么是数组及其应用的场景&#xff0c;掌握数组声明及访问的语法&#xff0c;具备利用数组渲染柱形图表的能力 今日重点&#xff1a; 循环嵌套数组综合案例 今日单词&#xff1a; 循环嵌套 利用循环的知识来对比一个简单的天文知识&#xff0c;我们…

Scrapy框架之MongoDB通过配置文件管理参数--Linux安装MongoDB--图形管理工具

目录 MongoDB通过配置文件 问题 解决方案 步骤 提示 Linux安装MongoDB 环境 下载依赖与安装包 解压安装 MongoDB GUI管理工具 独立软件GUI软件 Robo 3T使用 VSCode集成GUI插件 MongoDB通过配置文件 问题 启动MongoDB时&#xff0c;编写参数太麻烦 解决方案 通过配…

android h5 宿舍报修管理系统myeclipse开发mysql数据库编程服务端java计算机程序设计

一、源码特点 android h5 宿舍报修管理系统是一套完善的WEBandroid设计系统&#xff0c;对理解JSP java&#xff0c;安卓app编程开发语言有帮助&#xff08;系统采用web服务端APP端 综合模式进行设计开发&#xff09;&#xff0c;系统具有完整的 源代码和数据库&#xff0c;系…

docker网络

一、docker网络概述 1、docker网络实现的原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c; 同时Docker网桥是 每个容器的…

SNMP 计算机网络管理 实验3(二)SNMP协议工作原理验证与分析

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…