【server组件】——mysql连接池的实现原理

目录

1.池化技术

2.数据库连接池的定义

3.为什么要使用连接池

4. 数据库连接池的运行机制

5. 连接池与线程池的关系 

6. CResultSet的设计

 6.1构造函数

7. CDBConn的设计

6.1.构造函数 

6.2.init——初始化连接

8.数据库连接池的设计要点

9.接口设计

9.1 构造函数

9.2 请求获取连接 

9.3 释放连接

9.4 析构连接

9.5 mysql重连机制

10. 连接池数量设置

经验公式

实际分析

1.池化技术

池化技术能够 减少资源对象的创建次数提高程序的响应性能,特别是在 高并发下这种提高更加明显。 使用池化技术缓存的资源对象有如下共同特点:

1. 对象创建时间长;

2. 对象创建需要大量资源;

3. 对象创建后可被重复使用

像常见的 线程池、内存池、连接池、对象池 都具有以上的共同特点。

2.数据库连接池的定义

数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成 一个连接池,由程序 动态地对池中的连接进行 申请,使用,释放

3.为什么要使用连接池

1.资源的复用

由于 数据库 连接得到复用,避免了 频繁的创建、释放连接引起的性能开销,在减少系统消耗的基础 上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数 量)。

2.更快的响应速度

数据库连接池在初始化过程中,已经创建了若干数据库连接置于池中备用。对于业务请求处理而言,直接利用现有可用连接避免了从数据库连接初始化和释 放过程的开销,从而缩减了响应时间,提高了响应速度

3. 统一的连接管理,避免数据库连接泄露

在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而 避免了常规数据库连接操作中可能出现的资源泄露

4. 数据库连接池的运行机制

1. 从连接池获取或创建可用连接

2. 使用完毕之后,把连接返回给连接池

3. 在系统关闭前,断开所有连接并释放连接占用的系统资源

5. 连接池与线程池的关系 

当线程池中 某个线程 执行任务的时候 需要访问数据库的时候,需要从连接池中获取到空闲的连接,任务处理完毕后,线程 再将连接归还给连接池。

线程池与连接池的区别:

线程池:主动操作,主动获取任务并执行任务

连接池:被动操作,池的对象被任务获取,执行完任务后归还

6. CResultSet的设计

// 返回结果 select的时候用
class CResultSet {
public:CResultSet(MYSQL_RES* res);virtual ~CResultSet();bool Next();//获取下一行数据int GetInt(const char* key); //获取int类型的value值char* GetString(const char* key);//获取字符串类型的value值
private:int _GetIndex(const char* key);MYSQL_RES* 			m_res;MYSQL_ROW			m_row;map<string, int>	m_key_map;
};

 6.1构造函数


//将所有行数据存储到 m_row中,将字段名存储到 m_key_map,并记录其id
CResultSet::CResultSet(MYSQL_RES *res)
{m_res = res;// map table field key to index in the result arrayint num_fields = mysql_num_fields(m_res);MYSQL_FIELD *fields = mysql_fetch_fields(m_res);for (int i = 0; i < num_fields; i++){// 将表头数据存储到m_key_map中m_key_map.insert(make_pair(fields[i].name, i));}
}

根据字段名获取 下标 

int CResultSet::_GetIndex(const char *key)
{map<string, int>::iterator it = m_key_map.find(key);if (it == m_key_map.end()){return -1;}else{return it->second;}
}//根据key 获取 int 类型 value值
int CResultSet::GetInt(const char *key)
{int idx = _GetIndex(key);if (idx == -1){return 0;}else{return atoi(m_row[idx]); // 有索引}
}//根据 key值获取 字符串类型 value值
char *CResultSet::GetString(const char *key)
{int idx = _GetIndex(key);if (idx == -1){return NULL;}else{return m_row[idx];		// 列}
}//获取下一行数据
bool CResultSet::Next()
{m_row = mysql_fetch_row(m_res);if (m_row){return true;}else{return false;}
}

7. CDBConn的设计

CDBConn是一个 自主封装的 MySQL连接,从连接池中获取到的连接时一个CDBConn对象类型。

class CDBConn {
public:CDBConn(CDBPool* pDBPool);virtual ~CDBConn();int Init();// 创建表bool ExecuteCreate(const char* sql_query);// 删除表bool ExecuteDrop(const char* sql_query);// 查询CResultSet* ExecuteQuery(const char* sql_query);bool ExecuteUpdate(const char* sql_query, bool care_affected_rows = true);uint32_t GetInsertId();// 开启事务bool StartTransaction();// 提交事务bool Commit();// 回滚事务bool Rollback();// 获取连接池名const char* GetPoolName();MYSQL* GetMysql() { return m_mysql; }
private:CDBPool* 	m_pDBPool;	// to get MySQL server informationMYSQL* 		m_mysql;	// 对应一个连接
};

6.1.构造函数 

CDBConn::CDBConn(CDBPool *pPool)
{m_pDBPool = pPool;m_mysql = NULL;
}

6.2.init——初始化连接

int CDBConn::Init()
{m_mysql = mysql_init(NULL);	// mysql_标准的mysql c client对应的apiif (!m_mysql){log_error("mysql_init failed\n");return 1;}my_bool reconnect = true;mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect);	// 配合mysql_ping实现自动重连mysql_options(m_mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4");	// utf8mb4和utf8区别// ip 端口 用户名 密码 数据库名if (!mysql_real_connect(m_mysql, m_pDBPool->GetDBServerIP(), m_pDBPool->GetUsername(), m_pDBPool->GetPasswrod(),m_pDBPool->GetDBName(), m_pDBPool->GetDBServerPort(), NULL, 0)){log_error("mysql_real_connect failed: %s\n", mysql_error(m_mysql));return 2;}return 0;
}

8.数据库连接池的设计要点

连接池的设计思路:

1.连接到数据库,涉及到 数据库ip、端口、用户名、密码、数据库名字等;

  •  配置最小的连接数和 最大连接数

2.定义一个队列管理 连接

3.获取连接对象的 接口

4.归还连接对象的接口

5. 定义连接池的名称

class CDBPool {	// 只是负责管理连接CDBConn,真正干活的是CDBConn
public:CDBPool() {}CDBPool(const char* pool_name, const char* db_server_ip, uint16_t db_server_port,const char* username, const char* password, const char* db_name, int max_conn_cnt);virtual 	~CDBPool();int 		Init();		// 连接数据库,创建连接CDBConn* 	GetDBConn(const int timeout_ms = 0);	// 获取连接资源void 		RelDBConn(CDBConn* pConn);	// 归还连接资源const char* GetPoolName() { return m_pool_name.c_str(); }const char* GetDBServerIP() { return m_db_server_ip.c_str(); }uint16_t 	GetDBServerPort() { return m_db_server_port; }const char* GetUsername() { return m_username.c_str(); }const char* GetPasswrod() { return m_password.c_str(); }const char* GetDBName() { return m_db_name.c_str(); }
private:string 		m_pool_name;	// 连接池名称string 		m_db_server_ip;	// 数据库ipuint16_t	m_db_server_port; // 数据库端口string 		m_username;  	// 用户名string 		m_password;		// 用户密码string 		m_db_name;		// db名称int			m_db_cur_conn_cnt;	// 当前启用的连接数量int 		m_db_max_conn_cnt;	// 最大连接数量list<CDBConn*>	m_free_list;	// 空闲的连接list<CDBConn*>	m_used_list;		// 记录已经被请求的连接std::mutex m_mutex;std::condition_variable m_cond_var;bool m_abort_request = false;// CThreadNotify	m_free_notify;	// 信号量
};

9.接口设计

9.1 构造函数

CDBPool::CDBPool(const char *pool_name, const char *db_server_ip, uint16_t db_server_port,const char *username, const char *password, const char *db_name, int max_conn_cnt)
{m_pool_name = pool_name;m_db_server_ip = db_server_ip;m_db_server_port = db_server_port;m_username = username;m_password = password;m_db_name = db_name;m_db_max_conn_cnt = max_conn_cnt;	// m_db_cur_conn_cnt = MIN_DB_CONN_CNT; // 最小连接数量
}

9.2 Init 初始化

创建固定的最小的连接数,并将连接存放到 空闲队列中。


int CDBPool::Init()
{// 创建固定最小的连接数量for (int i = 0; i < m_db_cur_conn_cnt; i++){CDBConn *pDBConn = new CDBConn(this);int ret = pDBConn->Init();if (ret){delete pDBConn;return ret;}m_free_list.push_back(pDBConn);}// log_info("db pool: %s, size: %d\n", m_pool_name.c_str(), (int)m_free_list.size());return 0;
}

9.2 请求获取连接 

请连接流程

CDBConn *CDBPool::GetDBConn(const int timeout_ms)
{std::unique_lock<std::mutex> lock(m_mutex);//m_abort_request 判断连接池是否被终止if(m_abort_request) {log_warn("have aboort\n");return NULL;}if (m_free_list.empty())		// 当没有连接可以用时{// 第一步先检测 当前连接数量是否达到最大的连接数量 if (m_db_cur_conn_cnt >= m_db_max_conn_cnt){// 如果已经到达了,看看是否需要超时等待if(timeout_ms <= 0)		// 死等,直到有连接可以用 或者 连接池要退出{log_info("wait ms:%d\n", timeout_ms);m_cond_var.wait(lock, [this] {// log_info("wait:%d, size:%d\n", wait_cout++, m_free_list.size());// 当前连接数量小于最大连接数量 或者请求释放连接池时退出return (!m_free_list.empty()) | m_abort_request;});} else {// return如果返回 false,继续wait(或者超时),  如果返回true退出wait// 1.m_free_list不为空// 2.超时退出// 3. m_abort_request被置为true,要释放整个连接池m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] {// log_info("wait_for:%d, size:%d\n", wait_cout++, m_free_list.size());return (!m_free_list.empty()) | m_abort_request;});// 带超时功能时还要判断是否为空if(m_free_list.empty()) 	// 如果连接池还是没有空闲则退出{return NULL;}}if(m_abort_request) {log_warn("have aboort\n");return NULL;}}else // 还没有到最大连接则创建连接{CDBConn *pDBConn = new CDBConn(this);	//新建连接int ret = pDBConn->Init();if (ret){log_error("Init DBConnecton failed\n\n");delete pDBConn;return NULL;}else{m_free_list.push_back(pDBConn);m_db_cur_conn_cnt++;// log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);}}}CDBConn *pConn = m_free_list.front();	// 获取连接m_free_list.pop_front();	// STL 吐出连接,从空闲队列删除m_used_list.push_back(pConn);//将连接存放到被使用的 链表中return pConn;
}

9.3 释放连接

void CDBPool::RelDBConn(CDBConn *pConn)
{std::lock_guard<std::mutex> lock(m_mutex);list<CDBConn *>::iterator it = m_free_list.begin();for (; it != m_free_list.end(); it++)	// 避免重复归还{if (*it == pConn)	{break;}}if (it == m_free_list.end()){m_used_list.remove(pConn);m_free_list.push_back(pConn);m_cond_var.notify_one();		// 通知取队列} else {log_error("RelDBConn failed\n");}
}

9.4 析构连接

CDBPool::~CDBPool()
{std::lock_guard<std::mutex> lock(m_mutex);m_abort_request = true;m_cond_var.notify_all();		// 通知所有在等待的线程for (list<CDBConn *>::iterator it = m_free_list.begin(); it != m_free_list.end(); it++){CDBConn *pConn = *it;delete pConn;}m_free_list.clear();
}

9.5 mysql重连机制

1.设置重连机制属性

  //1. 设置启用(当发现连接断开时的)自动重连, 配合mysql_ping实现自动重连   
my_bool reconnect = true;
mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect);    

2. 检测连接是否正常

int STDCALL mysql_ping(MYSQL *mysql);

描述:

检查与服务端的连接是否正常。连接断开时,如果自动重新连接功能未被禁用,则尝试重新连接服务 器。该函数可被客户端用来检测闲置许久以后,与服务端的连接是否关闭,如有需要,则重新连接。

返回值:

连接正常,返回0;如有错误发生,则返回非0值。返回非0值并不意味着服务器本身关闭掉,也有可能 是网络原因导致网络不通。

10. 连接池数量设置

经验公式

连接数 = ((核心数 * 2) + 有效磁盘数)

按照这个公式,即是说你的服务器 CPU 是 4核 i7 的,那连接池连接数大小应该为 ((4*2)+1)=9

这里只是一个经验公式,还要和线程池数量以及业务结合在一起吗,根据经验公式的连接数的上下去测试连接池性能瓶颈。

实际分析

如果任务整体上是一个IO密集型的任务在处理一个请求的过程中(处理一个任务),总共耗时 100+5=105ms,而其中只有 5ms是用于计算操作的(消耗cpu),另外的100ms等待io响应,CPU利用 率为5/(100+5)。

使用线程池是为了尽量提高CPU的利用率,减少对CPU资源的浪费假设以100%的CPU利用率来说,要 达到100%的CPU利用率,对于一个CPU就要设置其利用率的倒数个数的线程数,也即 1/(5/(100+5))=21,4个CPU的话就乘以4。那么算下来的话,就是……84,这个时候线程池要设置84个线 程数,然后连接池的最大连接数量也是设置为84个连接

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

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

相关文章

【LeetCode】617.合并二叉树

题目 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重叠…

Azure资源命名和标记决策指南

参考 azure创建虚拟机在虚拟机中选择编辑标签&#xff0c;并添加标记&#xff0c;点击应用 3.到主页中转到所有资源 4. 添加筛选器并应用 5.查看结果&#xff0c;筛选根据给服务器定义的标签筛选出结果。 参考链接: https://learn.microsoft.com/zh-cn/azure/cloud-adoption…

【ArcGIS】经纬度数据转化成平面坐标数据

将点位置导入Gis中&#xff0c;如下&#xff08;经纬度表征位置&#xff09;&#xff1a; 如何利用Gis将其转化为平面坐标呢&#xff1f; Step1 坐标变换 坐标变换&#xff0c;打开ArcToolbox&#xff0c;找到“数据管理工具”->“投影和变换”->“要素”->“投影”…

JZ33二叉搜索树的后序遍历序列

题目地址&#xff1a;二叉搜索树的后序遍历序列_牛客题霸_牛客网 题目回顾&#xff1a; 解题思路&#xff1a; 使用栈 栈的特点是&#xff1a;先进后出。 通读题目后&#xff0c;我们可以得出&#xff0c;二叉搜索树是左子节点小于根节点&#xff0c;右子节点大于根节点。 …

【Tomcat】(Tomcat 下载Tomcat 启动Tomcat 简单部署 基于Tomcat进行网站后端开发)

文章目录 Tomcat下载Tomcat启动Tomcat简单部署 基于Tomcat进行网站后端开发 Tomcat Tomcat 是一个 HTTP 服务器.HTTP 协议就是 HTTP 客户端和 HTTP 服务器之间的交互数据的格式. HTTP 服务器我们可以通过 Java Socket 来实现. 而 Tomcat 就是基于 Java 实现的一个开源免费,也是…

CNN的特性

1、位移不变性 它指的是无论物体在图像中的什么位置&#xff0c;卷积神经网络的识别结果都应该是一样的。 因为CNN就是利用一个kernel在整张图像上不断步进来完成卷积操作的&#xff0c;而且在这个过程中kernel的参数是共享的。换句话说&#xff0c;它其实就是拿了同一张“通…

数据结构:选择排序

简单选择排序 选择排序是一种简单直观的排序算法。首先在未排序序列中找到最大&#xff08;最小&#xff09;的元素&#xff0c;存放到排序学列的其实位置&#xff0c;然后在剩余的未排序的元素中寻找最小&#xff08;最大&#xff09;元素&#xff0c;存放在已排序序列的后面…

cmake扩展(1)——VS+CMake创建Qt项目

创建项目 创建CMakeLists #cmake最低版本 cmake_minimum_required(VERSION 3.10) #项目名 project(regextool)#查找所有*.h,*.ui,*.cpp文件&#xff0c;并存入SOURCES中 file(GLOB SOURCES "*.cpp" "*.ui" "*.h")#开启moc set(CMAKE_AUTOMOC O…

计算机视觉中的特征检测和描述

一、说明 这篇文章是关于计算机视觉中特征检测和描述概念的简要理解。在其中&#xff0c;我们探讨了它们的定义、常用技术、简单的 python 实现和一些限制。 二、什么是特征检测和描述&#xff1f; 特征检测和描述是计算机视觉中的基本概念&#xff0c;在图像识别、对象跟踪和图…

Opencv特征检测之ORB算法原理及应用详解

Opencv特征检测之ORB算法原理及应用详解 特征是图像信息的另一种数字表达形式。一组好的特征对于在指定 任务上的最终表现至关重要。视觉里程 &#xff08;VO&#xff09; 的主要问题是如何根据图像特征来估计相机运动。但是&#xff0c;整幅图像用来计算分析通常比较耗时&…

机器学习终极指南:特征工程(02/2) — 第 -2 部分

接上文&#xff1a;机器学习终极指南&#xff1a;特征工程&#xff08;01/2&#xff09; 五、处理不平衡数据 处理不平衡的数据是机器学习的一个重要方面。不平衡数据是指目标变量的分布不均匀&#xff0c;并且与另一个类相比&#xff0c;一个类的代表性不足。这可能导致模型…

[内网渗透]CFS三层靶机渗透

文章目录 [内网渗透]CFS三层靶机渗透网络拓扑图靶机搭建Target10x01.nmap主机探活0x02.端口扫描0x03.ThinkPHP5 RCE漏洞拿shell0x04.上传msf后门(reverse_tcp)反向连接拿主机权限 内网渗透Target2&#xff08;1&#xff09;路由信息探测&#xff08;2&#xff09;msf代理配置&a…

两个数组的交集-C语言/Java

描述 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序。&#xff08;1 < nums1.length, nums2.length < 1000&#xff0c;0 < nums1[i], nums2[i] < 1000&#xff09; 示例1 输入…

yolov5代码解读之train.py【训练模型】

哇咔咔&#xff0c;登场 代码开头都是一些导包到模块的&#xff1a; 接下来来到入口函数&#xff1a; 我们直接来到main函数的内容&#xff1a;&#xff08;分四个部分&#xff09; 前两部分&#xff1a; 关于main函数的第二部分中的resume参数&#xff08;496行&#xff09;&…

概率图模型(Probabilistic Graphical Model,PGM)

概率图模型&#xff08;Probabilistic Graphical Model&#xff0c;PGM&#xff09;&#xff0c;是一种用图结构来描述多元随机变量之间条件独立性的概率模型。它可以用来表示复杂的概率分布&#xff0c;进行有效的推理和学习&#xff0c;以及解决各种实际问题&#xff0c;如图…

mysql延时问题排查

背景介绍 最近遇到一个奇怪的问题&#xff0c;有个业务&#xff0c;每天早上七点半产生主从延时&#xff0c;延时时间12.6K&#xff1b; 期间没有抽数/备份等任务&#xff1b;查看慢日志发现&#xff0c;期间有一个delete任务&#xff0c;在主库执行了161s delete from xxxx_…

人类:我觉得1+1=956446,你觉得呢?大模型:啊对对对

大模型太「听话」了怎么办&#xff1f; 大型语言模型&#xff08;LLM&#xff09;的自然语言理解与生成能力一直备受称赞&#xff0c;特别是 ChatGPT 等对话式语言模型能够与人类流畅、自然地进行多轮对话。然而&#xff0c;最近一篇 Google DeepMind 的论文研究发现 LLM 普遍存…

企业权限管理(八)-登陆使用数据库认证

Spring Security 使用数据库认证 在 Spring Security 中如果想要使用数据进行认证操作&#xff0c;有很多种操作方式&#xff0c;这里我们介绍使用 UserDetails 、 UserDetailsService来完成操作。 UserDetails public interface UserDetails extends Serializable { Collecti…

转义字符\

转移字符&#xff0c;就是通过字符&#xff0c;来转变原来字符的意思 常见的转义字符&#xff1a; 1、 2 注&#xff1a;" 的作用和他是类似的 3 4、 当打印\a时&#xff0c;电脑会出现一个警告&#xff0c;蜂鸣的声音 5、 阿斯克码表

机器学习---对数几率回归

1. 逻辑回归 逻辑回归&#xff08;Logistic Regression&#xff09;的模型是一个非线性模型&#xff0c; sigmoid函数&#xff0c;又称逻辑回归函数。但是它本质上又是一个线性回归模型&#xff0c;因为除去sigmoid映射函 数关系&#xff0c;其他的步骤&#xff0c;算法都是…