WebServer -- 数据库连接池

目录

🎂基础知识

🚩整体内容

🌼单例模式创建

🎂连接池(代码实现)

初始化

获取 && 释放连接

销毁连接池

🍑RAII 机制释放数据库连接

定义

实现


🎂基础知识

什么是数据库连接池?

池是一组资源的集合,这组资源在服务器启动之初,就被完全创建好并初始化

通俗来说,池是资源的容器,本质上是对资源的复用

顾名思义,连接池中的资源为一组数据库连接,由程序动态的对池中的连接进行使用 / 释放

当系统开始处理客户请求时,如果它需要相关的资源,可以直接从池中获取,无需动态分配;

当服务器处理完一个客户连接后,可以把相关资源放回池中,无需执行系统调用释放资源

数据库访问的一般流程是什么?

当系统需要访问数据库时,先系统创建数据库连接,完成数据库操作,然后系统断开数据库连接

为什么要创建连接池?

从一般流程看,若系统需要频繁访问数据库,则需要频繁创建和断开数据库连接,

而创建数据库连接,是一个很耗时的操作,也容易对数据库造成安全隐患

在程序初始化的时候,集中创建多个数据库连接,并把它们集中管理,供程序使用,可以保证较快的数据库读写速度,更加安全可靠

🚩整体内容

概述

池可以看作资源的容器,有多种实现方法:数组,链表,队列...

这里,使用 单例模式 和 链表 创建数据库连接池,实现对数据库连接资源的复用

TinyWebserver 中,数据库模块分 2 部分:

1)数据库连接池的定义

2)利用连接池完成登录注册的校验功能

具体地,工作线程从数据库连接池取得一个连接,访问数据库中的数据,访问完毕后将连接交还连接池

内容

本博客介绍数据库连接池的定义,具体涉及到:单例模式的创建,连接池代码的实现,RAII 机制释放数据库连接

  • 单例模式创建
    描述连接池的单例实现
  • 连接池代码实现
    对连接池的外部外文接口的理解
  • RAII 机制释放数据库连接
    描述连接释放的封装逻辑

🌼单例模式创建

使用局部静态变量懒汉模式创建连接池

class connection_pool
{
public:// 局部静态变量单例模式static connection_pool *GetInstance();private:connection_pool();~connection_pool();
};// 类外实现
connection_pool *connection_pool::GetInstance()
{static connection_pool connPool;return &connPool;
}

🎂连接池(代码实现)

连接池的定义中注释比较详细,这里仅对其实现进行解析

连接池的功能:初始化,获取连接,释放连接,销毁连接池

初始化

值得注意的是,销毁连接池没有直接被外部调用,而是通过 RAII 机制来完成自动释放;

使用信号量实现多线程争夺连接的同步机制,这里将信号量初始化为数据库的连接总数

// 构造函数,初始化连接池中的连接数量
connection_pool::connection_pool()
{this->CurConn = 0; // 当前连接数this->FreeConn = 0; // 空闲连接数
}// 析构函数,销毁连接池
connection_pool::~connection_pool()
{DestroyPool(); // 销毁连接池
}// 初始化连接池
void connection_pool::init(string url, string User, string PassWord,string DBName, int Port, unsigned int MaxConn)
{// 初始化数据库信息this->url = url; // 数据库地址this->Port = Port; // 数据库端口this->User = User; // 用户名this->PassWord = PassWord; // 密码this->DatabaseName = DBName; // 数据库名称// 创建 MaxConn 条数据库连接for (int i = 0; i < MaxConn; i++) {MYSQL *con = NULL; // MySQL 连接指针con = mysql_init(con); // 初始化连接if (con == NULL) {cout << "Error:" << mysql_error(con); // 输出错误信息exit(1); // 退出程序}con = mysql_real_connect(con, url.c_Str(), User.c_str(),DBName.c_str(), Port, NULL, 0); // 连接数据库if (con == NULL) {cout << "Error: " << mysql_error(con); // 输出错误信息exit(1); // 退出程序}// 更新连接池和空闲连接数量connList.push_back(con); // 将连接添加到连接池列表++FreeConn; // 空闲连接数加一}// 信号量初始化为最大连接数reserve = sem(FreeConn);this->MaxConn = FreeConn; // 最大连接数等于空闲连接数
}

获取 && 释放连接

当线程数量大于数据库连接数量,使用信号量进行同步,每次取出连接,信号量原子 -1,释放连接原子 +1;若连接池内没有连接了,则阻塞等待

另外,由于多线程操作连接池,会造成竞争,这里用 互斥锁 完成同步,具体的同步机制均使用 lock.h 中封装好的类

// 当有请求时,从数据库连接池返回一个可用连接,
// 更新使用和空闲连接数
MYSQL *connection_pool::GetConnection()
{MYSQL *con = NULL; // MySQL 连接指针if (0 == connList.size()) // 如果连接池为空,返回空指针return NULL;// 取出连接,信号量原子 -1,为 0 则等待reserve.wait(); // 等待信号量lock.lock(); // 加锁con = connList.front(); // 获取连接池中的第一个连接connList.pop_front(); // 弹出连接// 这里两个变量,没有用到,鸡肋啊...--FreeConn; // 空闲连接数减一++CurConn; // 当前连接数加一lock.unlock(); // 解锁return con; // 返回连接
}// 释放当前使用的连接
bool connection_pool::ReleaseConnection(MYSQL *con)
{if (NULL == con) // 如果连接为空,返回falsereturn false;lock.lock(); // 加锁connList.push_back(con); // 将连接放回连接池++FreeConn; // 空闲连接数加一--CurConn; // 当前连接数减一lock.unlock(); // 解锁// 释放连接原子 +1reserve.post(); // 释放信号量return true; // 返回true
}

销毁连接池

1)通过 迭代器 遍历连接池链表

2)关闭对应数据库连接

3)清空链表

4)并重置空闲连接和现有连接数量

// 销毁数据库连接池
void connection_pool::DestroyPool()
{lock.lock(); // 加锁if (connList.size() > 0) { // 如果连接池不为空// 迭代器遍历,关闭数据库连接list<MYSQL *>::iterator it; // 声明迭代器it,用于遍历connList列表for (it = connList.begin(); it != connList.end(); ++it) // 遍历连接池中的每个连接{MYSQL *con = *it; // 获取迭代器指向的连接mysql_close(con); // 关闭数据库连接}CurConn = 0; // 将当前连接数设置为0FreeConn = 0; // 将空闲连接数设置为0// 清空listconnList.clear(); // 清空连接池列表lock.unlock(); // 解锁}// 无论是否进入 if 分支,都能正确释放互斥锁lock.unlock(); // 解锁
}

🍑RAII 机制释放数据库连接

数据库连接的获取与释放通过 RAII 机制封装,避免手动释放

定义

需要注意的是,获取连接时,通过有参构造对传入的参数进行修改;

其中,数据库连接本身是指针类型,所以参数需要通过双指针才能对其进行修改

双指针:指向指针的指针,避免暴露指针内部细节 

class connectionRAII {
public:// 双指针对 MYSQL *con 修改connectionRAII(MYSQL **con, connection_pool *connPool); // 构造函数,传入双指针用于修改MYSQL *con~connectionRAII(); // 析构函数private:MYSQL *conRAII; // 数据库连接指针connection_pool *poolRAII; // 连接池指针
};

实现

 不直接调用  获取和释放连接的接口,将其封装起来,通过 RAII 机制进行获取和释放

connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool)
{// 通过双指针传入的MYSQL **SQL,将其指向连接池中获取的连接*SQL = connPool->GetConnection();// 将获取到的连接赋值给conRAII数据库连接指针conRAII = *SQL;// 将传入的连接池指针赋值给poolRAIIpoolRAII = connPool;
}// 析构函数,用于释放连接
connectionRAII::~connectionRAII()
{// 通过连接池指针释放连接conRAIIpoolRAII->ReleaseConnection(conRAII);
}

 

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

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

相关文章

使用 Docker 部署 Answer 问答平台

1&#xff09;介绍 GitHub&#xff1a;https://github.com/apache/incubator-answer Answer 问答社区是在线平台&#xff0c;让用户提出问题并获得回答。用户可以发布问题并得到其他用户的详细答案、建议或信息。回答可以投票或评分&#xff0c;有助于确定有用的内容。标签和分…

Ps:历史记录面板

Ps菜单&#xff1a;窗口/历史记录 Window/History 历史记录 History面板提供了对图像编辑过程中所进行更改的深入控制&#xff0c;可以让用户回溯并查看每一步操作&#xff0c;从而允许用户轻松撤销错误或比较不同的编辑效果。 ◆ ◆ ◆ 常用操作方法与技巧 “历史记录”面板…

CentOS7设置虚拟机语言为中文

1.查看本地安装的语言 locale -a 是一个Linux命令&#xff0c;用于列出系统中可用的所有区域设置&#xff08;locales&#xff09;它包含了各种语言和地区的不同设置。 打开终端&#xff08;右键open terminal&#xff09;输入 locale -a 查看本地安装的语言&#xff1a; 其中z…

如何在Unity项目中使用Plastic SCM进行版本控制

引言 Plastic SCM是一个版本控制系统&#xff0c;专为处理大型项目而设计&#xff0c;特别适用于游戏开发中的Unity项目。它提供了强大的分支和合并工具&#xff0c;使团队能够高效地协作开发。 安装和设置 安装Plastic SCM 访问Plastic SCM官网下载客户端。根据您的操作系…

一些可以访问gpt的方式

1、Coze扣子是新一代 AI 大模型智能体开发平台。整合了插件、长短期记忆、工作流、卡片等丰富能力&#xff0c;扣子能帮你低门槛、快速搭建个性化或具备商业价值的智能体&#xff0c;并发布到豆包、飞书等各个平台。https://www.coze.cn/ 2、https://poe.com/ 3、插件阿里…

EasyRecovery16电脑硬盘数据恢复软件功能详解

在数字化时代&#xff0c;人们在日常生活和工作中越来越依赖于电脑和移动设备。不管是个人用户还是企业&#xff0c;数据的重要性都不言而喻。然而&#xff0c;数据丢失和损坏的风险也随之增加&#xff0c;因此&#xff0c;数据恢复软件的需求也日益增长。 EasyRecovery 16是一…

不同材质的油封及其使用温度限制

油封&#xff0c;也称为旋转轴密封件&#xff0c;是防止润滑油从机器和轴承内部间隙泄漏的重要部件。油封的有效性很大程度上取决于其承受运行过程中所暴露温度的能力。 材料问题&#xff1a;不同材料及其温度限制 制造油封所使用的不同材料可以承受不同的温度范围。这里有一…

【打工日常】使用docker部署在线Photopea用于linux下替代ps

一、Photopea介绍 linux没有ps适配&#xff0c;对于有时候工作来说确实不方便&#xff0c;我找了很久&#xff0c;才找到了一款功能可以跟ps接近的在线软件&#xff0c;使用docker部署就可以了。它是ps的最佳替代品之一&#xff0c;其界面几乎与ps相同&#xff0c;只不过它是在…

Springboot+vue的医药管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的医药管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

云轴科技ZStack与华东师范大学共建产教融合基地

近日&#xff0c;上海云轴信息科技有限公司&#xff08;云轴科技ZStack&#xff09;与华东师范大学上海国际首席技术官学院宣布&#xff0c;共同打造产教融合基地&#xff0c;以促进人才培养与产业需求的全方位融合。这一举措旨在深化教育与产业的合作关系&#xff0c;培养更多…

实战 | 使用YOLOv8图像分割实现路面坑洞检测(步骤 + 代码)

导 读 本文主要介绍使用YOLOv8图像分割实现路面坑洞检测&#xff08;步骤 代码&#xff09;。 背 景 如上图所示&#xff0c;现实生活中路面坑洞对车辆和驾驶员安全来说存在巨大隐患&#xff0c;本文将介绍如何使用YoloV8图像分割技术来检测路面坑洞&#xff0c;从而提示驾…

计算机网络|Socket

文章目录 Socket并发socket Socket Socket是一种工作在TCP/IP协议栈上的API。 端口用于区分不同应用&#xff0c;IP地址用于区分不同主机。 以下是某一个服务器的socket代码。 其中with是python中的一个语法糖&#xff0c;代表当代码块离开with时&#xff0c;自动对s进行销毁…

JS:原型与原型链(附带图解与代码)

一、原型 写在前面&#xff1a; 任何对象都有原型。 函数也是对象&#xff0c;所以函数也有原型。 1.什么是原型 在 JavaScript 中&#xff0c;对象有一个特殊的隐藏属性 [[Prototype]]&#xff0c;它要么为 null&#xff0c;要么就是对另一个对象的引用&#xff0c;该对象…

什么是片内片间均匀性?

均匀性在芯片制程的每一个工序中都需要考虑到&#xff0c;包括薄膜沉积&#xff0c;刻蚀&#xff0c;光刻&#xff0c;cmp&#xff0c;离子注入等。较高的均匀性才能保证芯片的产品与性能。那么片内和片间非均匀性是什么&#xff1f;如何计算&#xff1f;有什么作用呢&#xff…

递归与递推(蓝桥杯 c++)

目录 题目一&#xff1a; 代码&#xff1a; 题目二: 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目一&#xff1a; 代码&#xff1a; #include<iostream> #include<cstring> using namespace std; int …

react Provider Consumer 使用方法

相关文章 React Context的使用方法 跨几个组件传递值或者方法的时候, 如果依赖父子组件传值, 那势必会很麻烦. 好在react提供了Provider 和 Consumer 1 调用react的createContext()方法, 产生生产者和消费者组件. // context.js import React from react let { Consumer, Pr…

node.js最准确历史版本下载

先进入官网:Node.js https://nodejs.org/en 嫌其他博客多可以到/release下载:Node.js,在blog后面加/release https://nodejs.org/en/blog/release/ 点击next翻页,同样的道理

数据结构:栈和队列(队列)

队列的性质 一端进,从另一端出,先进的数据一定先出去,进数据的一端叫队尾,出数据的一端叫队头 特点 保障公平性的排队 #pragma once #include<stdlib.h> #include<stdbool.h> #include<assert.h>typedef int QDataType; typedef struct QueueNode {int v…

设计模式-结构型模式-外观模式

外观模式&#xff08;Facade&#xff09;&#xff0c;为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。[DP] 首先&#xff0c;定义子系统的各个组件接口和具体实现类&#xff1a; // 子系统组件接…

【C++】深入理解C++虚函数与纯虚函数

文章目录 一、虚函数&#xff08;Virtual Function&#xff09;1.1 定义和作用1.2 实现原理1.3 示例代码1.4 虚函数的重写定义规则注意事项示例 1.5 基类和派生类的虚函数表**示例理解** 二、纯虚函数&#xff08;Pure Virtual Function&#xff09;2.1 定义和作用2.2 示例代码…