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

news2024/11/20 20:36:19

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

本文章所有代码大都来自开源项目: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.coloradmin.cn/o/680532.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker中搭建RabbitMQ集群

Docker中搭建RabbitMQ集群 1、启动三个RabbitMQ容器2、为容器设置节点2.1、设置Erlang Cookie2.2、设置节点12.3、设置节点22.4、设置节点32.5、预览结果 3、配置镜像队列3.1、配置镜像的原因3.2、搭建步骤 1、启动三个RabbitMQ容器 服务器IP端口hostname管理界面地址192.168.…

IoC容器的设计(利用反射、注解和工厂模式实现)

1.实验要求 利用注解、反射和工厂模式设计一个简单的IoC容器该IoC容器包含3个注解和一个IoC容器类&#xff08;AnnotationConfigApplicationContext&#xff09;&#xff0c;其定义如下&#xff1a; 注解&#xff1a; 注解含义Component标注BeanAutowired标注需要被注入的对…

如何写好一份企业直播主题策划?

写一份好的直播主题策划&#xff0c;需要考虑包括目标受众、目的、内容、形式、互动等&#xff0c;下面是写企业直播主题策划的一些关注点&#xff0c;希望能帮到您。 定位您直播的目标受众 明确你的直播主题适合的目标受众是谁&#xff0c;他们的兴趣、需求和期望是什么。了解…

OAuth2,jwt,springsecurity之间的区别和联系

OAuth 2.0、JWT (JSON Web Token) 和 Spring Security 是安全相关的概念和技术&#xff0c;它们有着不同的功能和用途。 OAuth 2.0&#xff08;开放授权&#xff09;&#xff1a; OAuth 2.0 是一种授权框架&#xff0c;用于授权第三方应用程序访问用户资源&#xff0c;而无需共…

【OpenCV DNN】Flask 视频监控目标检测教程 10

欢迎关注『OpenCV DNN Youcans』系列&#xff0c;持续更新中 【OpenCV DNN】Flask 视频监控目标检测教程 10 3.10 OpenCV DNNFlask实时监控目标检测1、加载MobileNet SSD模型2、导入分类名称文件3、处理视频帧进行目标检测4、新建一个Flask项目5、Python 程序文件6、视频流的网…

linux系统addr ip以及ifconfig查询不到ip地址解决方法,没有ens33

先看使用情况 网上一堆垃圾博文解决方案都是你抄我我抄你&#xff0c;一点用没有&#xff0c;都说使用 vi /etc/sysconfig/network-scripts/ifcfg-ens33 来更改配置ONBOOT为yes&#xff0c;改个屁&#xff0c;给你们看看我目前的配置&#xff0c;劳资本身就是yes&#xff0c;还…

Elasticsearch 基本使用(二)简单查询 嵌套查询

查询数据 简单查询按id查询单条记录查询所有数据设置排序filter 过滤查询数组内的值查询 嵌套查询查询一个外层字段 内的嵌套字段查询多个字段&#xff0c;其中有嵌套字段 简单查询 按id查询单条记录 GET bank/_doc/1查询所有数据 默认只查询10条记录 GET bank/_search {&q…

Linux任务调度、磁盘分区、挂载

一、任务调度介绍 任务调度是指系统在某个时间执行的特定的命令或程序 任务调度分为两类&#xff1a; 1.系统工作&#xff1a;有些重要的工作必须周而复始的执行&#xff0c;比如病毒扫描 2&#xff0c;个别用户工作&#xff1a;个别用户可能希望执行某些程序&#xff0c;比如…

canvas自定义绘制顺序解决遮挡问题

canvas自定义绘制顺序解决遮挡问题 1. 问题场景2. 解决思路3. 实现代码 1. 问题场景 使用canvas绘制进行要素叠加时&#xff0c;往往会出现不是按照先画的在下面&#xff0c;后画的在最上面这样的顺序进行叠加显示。原因就是由于图片大小不同导致绘制或加载的时间不一样&#…

合宙Air724UG Cat.1模块硬件设计指南--LCD专用SPI接口

概述 Air724UG支持一路LCD专用SPI接口&#xff0c;用于驱动SPI LCD屏幕&#xff0c;不能作为通用SPI使用 特性&#xff1a; 最大支持320240分辨率&#xff0c;30帧 内置图像处理单元GOUDA 支持格式&#xff1a; YUV4 : 2 : 0 ;YUV4 : 2 : 2;RGB565; ARGB8888 目前只支持4线8bi…

MySQL - 第8节 - MySQL复合查询

1.基本查询回顾 准备测试表&#xff1a; • 下面给出三张表&#xff0c;分别是员工表&#xff08;emp&#xff09;、部门表&#xff08;dept&#xff09;和工资等级表&#xff08;salgrade&#xff09;。 • 后续所要进行的查询操作都将以这三张表作为数据源&#xff0c;包括基…

【论文笔记】BEIT:BERT PRE-TRAINING OF IMAGE TRANSFORMERS

GitHub 1.介绍 1.1 挑战 视觉转换器的输入单元&#xff0c;即图像补丁&#xff0c;没有预先存在的词汇。预测遮罩面片的原始像素往往会在预训练短程依赖性和高频细节上浪费建模能力 1.2 回顾一下Bert的基本架构和流程 输入编码&#xff1a;通过tokenizer将输入的文本中的每…

gmpy2

简介 gmpy2是一个Python扩展模块&#xff0c;是对GMP的封装&#xff0c;它的前身是gmpy。 GMP&#xff08;即GNU高精度算术运算库&#xff09;&#xff0c;它是一个开源的高精度运算库&#xff0c;其中不但有普通的整数、实数、浮点数的高精度运算&#xff0c;还有随机数生成&a…

【promptulate专栏】使用ChatGPT和XMind快速构建思维导图

本文节选自笔者博客&#xff1a;https://www.blog.zeeland.cn/archives/ao302950h3j &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#…

Go语言基础:标识符、关键字、变量、常量、iota

一、标识符 在编程语言中标识符就是程序员定义的具有特殊意义的词&#xff0c;比如变量名、常量名、函数名等等。 Go语言中标识符由字母数字和_(下划线&#xff09;组成&#xff0c;并且只能以字母和_开头。 二、关键字 关键字是指编程语言中预先定义好的具有特殊含义的标识符…

ThreadPoolExecutor的应用和源码分析

前面描述的线程池的创建方式大都是Executors类中创建出来&#xff0c;基于ThreadPoolExecutor去new出来实现的。 我们为什么要自定义线程池 在线程池ThreadPoolExecutor中提供了7个参数&#xff0c;都作为非常核心的属性&#xff0c;在线程池去执行任务的时候&#xff0c;每个…

【Docker】容器化和虚拟化基础

Docker发展史 Jail(监狱)时代 1979 年 贝尔实验室发明 chroot chroot的设计原理是&#xff1a;把一个进程的文件系统隔离起来。 ​ chroot 系统调用可以将进程及其子进程的根目录更改为文件系统中的新位置。隔离以后&#xff0c;该进程无法访问到外面的文件&#xff0c;因此这…

管理类联考——逻辑——知识篇——论证推理——二、加强——haimian

考点分析 加强 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量213325356 主要问法 以下哪项如果为真&#xff0c;最能加强上述结论的说服力?以下哪项如果为真&#xff0c;最能支持题干的论证? 解题思路 阅读问题&#xff0c;确定是否为加强题型&…

进程参数编程

问题 execve(...) 的参数分别是什么&#xff1f;有什么意义&#xff1f; 第一个参数是程序路径&#xff0c;第二个参数是进程参数&#xff0c;第三个参数是环境变量 再论 execve(...) main 函数 (默认进程入口) int main(int argc, char* argv[]) argc - 命令行参数个数argv…

java——jdbc编程

文章目录 JDBC的概念JDBC的常用APIJDBC示例代码PreparedStatementCallableStatement JDBC&#xff08;Java Database Connectivity&#xff09;是Java的一种数据库访问标准&#xff0c;它提供了一套API&#xff0c;使得我们可以通过Java程序来访问和操作各种关系型数据库。 下面…