【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个连接
相关文章:
【server组件】——mysql连接池的实现原理
目录 1.池化技术 2.数据库连接池的定义 3.为什么要使用连接池 4. 数据库连接池的运行机制 5. 连接池与线程池的关系 6. CResultSet的设计 6.1构造函数 7. CDBConn的设计 6.1.构造函数 6.2.init——初始化连接 8.数据库连接池的设计要点 9.接口设计 9.1 构造函数 …...
DSP开发:串口sci的发送与接收实现
DSP开发:串口sci的发送与接收实现 文章目录 DSP开发:串口sci的发送与接收实现串口配置串口SCI初始化详细分析串口SCI使用 串口配置 /*--------------------------------------------scia----------------------------*/ /*----------------------------…...
实训一 :Linux的启动、关机及登录
实训一 :Linux的启动、关机及登录 2017 年 2 月 22 日 今日公布 实训目标 完成本次实训,将能够: 描述Linux的开机过程。在图形模式和文本模式下登录Linux。关闭和重启Linux 实训准备 一台已安装RHEL6的虚拟计算机,Linux虚拟…...
Redis分布式锁问题
1、业务单机情况下 问题:并发没有加锁导致线程安全问题。 解决方法:加锁处理,如lock、synchronized 仍有问题:业务分布式情况下,代码级别加锁已经无效。需要借助第三方组件,如redis、zookeeper。 2、业务分…...
windows安装apache-jmeter-5.6.2教程
目录 一、下载安装包(推荐第二种) 二、安装jmeter 三、启动jmeter 一、下载安装包(推荐第二种) 1.官网下载:Apache JMeter - Download Apache JMeter 2.百度云下载:链接:https://pan.baidu.…...
密码检查-C语言/Java
描述 小明同学最近开发了一个网站,在用户注册账户的时候,需要设置账户的密码,为了加强账户的安全性,小明对密码强度有一定要求: 1. 密码只能由大写字母,小写字母,数字构成; 2. 密码不…...
基于Matlab实现心电信号小波特征提取和对应疾病识别仿真(附上源码+数据集)
本文基于Matlab平台,研究了心电信号的小波特征提取方法,并应用于心电信号疾病识别仿真实验中。首先,介绍了心电信号的基本特征和常见的心电疾病。然后,详细阐述了小波变换的原理和方法,并提出了一种基于小波分解和小波…...
第五十二天
HTML5 ●MathML 是数学标记语言,是一种基于XML(标准通用标记语言的子集)的标准,用来在互联网上书写数学符号和公式的置标语言。 ●拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置。 在 HTML5 中…...
爬虫练手项目——获取龙族小说全文
网站信息 目标网站信息如下:包含了龙族1-5全部内容 代码 import requests from bs4 import BeautifulSoup import os import timeheaders {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Sa…...
OpenCV图像处理——几何变换
目录 图像缩放图像平移图像旋转图像的仿射变换透射变换图像金字塔 图像缩放 cv.resize(src,dsize,fx0,fy0,interpolationcv2.INTER_LINEAR)import numpy as np import cv2 as cv import matplotlib.pyplot as pltkidscv.imread(./汪学长的随堂资料/4/图像操作/dog.jpg) plt.im…...
Apache JMeter:完全指南
Apache JMeter 是一款开源的性能测试工具,可以用于测试 Web 应用程序、FTP 服务器、数据库等各种类型的服务器。本文将以 JMeter 5.5 为例介绍 JMeter 的使用方法。 下载和安装 由于 JMeter 是使用 Java 开发的,因此在运行之前必须先安装 JDK。您可以在…...
js obj 生成java 实体 entity
js obj 生成java 实体 entity D:\proj\js\js-util\gen_java.js 文档:js obj 生成java 实体 entity.note 链接:http://note.youdao.com/noteshare?idc61fbd4b8684dc41c8c9ab42107d5f87&subE7484F5F277041578A2E58FDC6320BA6 添加链接描述 class S…...
【第二阶段】kotlin语言的匿名函数与具名函数
fun main() {//匿名函数val niminginfoniming("kotlin",20,{"$it"})println(niminginfo)//具名函数 理解:showResult:(String)->String):StringshowResultImpl(result:String):Stringval juminginfoniming("c ",20,::showResultI…...
P1123 取数游戏
取数游戏 题目描述 一个 N M N\times M NM 的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻 8 8 8 个格子中的一个即认为这两个数字相邻),求…...
JavaScript高级:原型与原型链继承方式
在 JavaScript 中,继承是一种重要的概念,它使得对象可以从其他对象继承属性和方法,实现代码的重用和扩展。原型与原型链是 JavaScript 中实现继承的核心机制,虽然听起来有些高深,但我们可以通过通俗易懂的方式来理解它…...
使用vue-grid-layout时 You may need an appropriate loader to handle this file type.
使用vue-grid-layout时 You may need an appropriate loader to handle this file type. node版本不匹配 我的node v14.16.0 vue-gride-layout 需要用 v 2.3.7的版本 卸载后重新安装即可...
C# 2048小游戏核心算法
文章目录 01.程序结构划分02.去零03.合并04.上移05.下移/左移/右移,只是取数据的方向不同06.提高可读性 01.程序结构划分 02.去零 有序向量“唯一化”的思路。 /// <summary>/// 去零/// </summary>/// <param name"row">对于一行或一…...
设计模式(5)代理模式
一、介绍: 【Subject/抽象角色】定义了RealSubject和Proxy的共用接口,这样就可以在任何使用RealSubject的地方都可以使用Proxy 【RealSubject/真实角色】定义Proxy所代表的真实实体 【Proxy/代理角色】保存一个引用使得代理可以访问实体,并…...
Django配置(部署环境较乱,暂时启用)
django配置 web服务器中部署项目及WSGI简介 web服务器 WSGI 在IIS中部署django项目 安装 wfastcgi :pip install wfastcgi安装IIS: 以上选择项勾选后确定 将CGI文件复制到项目中, 将项目复制到IIS默认目录中 部署IIS 添加变量信息如下…...
【设计模式】桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作为桥接的接口,使得…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
