移动论坛网站模板,wordpress媒体库略缩图,电商网站方案,巨野网站建设目录
#x1f382;基础知识
#x1f6a9;整体内容
#x1f33c;单例模式创建
#x1f382;连接池#xff08;代码实现#xff09;
初始化
获取 释放连接
销毁连接池
#x1f351;RAII 机制释放数据库连接
定义
实现 #x1f382;基础知识
什么是…
目录
基础知识
整体内容
单例模式创建
连接池代码实现
初始化
获取 释放连接
销毁连接池
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) { // 如果连接池不为空// 迭代器遍历关闭数据库连接listMYSQL *::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);
}