【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)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作为桥接的接口,使得…...

核心机制:流量控制
搭配滑动窗口使用的 窗口大小 窗口越大,传输速度就越快,但是也不能无限大,太大了,对于可靠性会有影响 比如发生方以非常快的速度,发送,接收方的处理速度跟不上,也就会导致有效数据被接受方丢弃(又得重传) 流量控制,就是根据接收方的处理能力(如何衡量?),干预到发送方的发送…...

深度强化学习赋能城市消防优化,中科院团队提出DRL新方法破解设施配置难题
在城市建设与发展中,地理空间优化至关重要。从工业园区选址,到公共服务设施布局,它都发挥着关键作用。但传统求解方法存在诸多局限,如今,深度学习技术为其带来了新的转机。 近日,在中国地理学会地理模型与…...
JVM——JVM中的字节码:解码Java跨平台的核心引擎
引入 在Java的技术版图中,字节码(Bytecode)是连接源代码与机器世界的黄金桥梁。当开发者写下第一行public class HelloWorld时,编译器便开始了一场精密的翻译工程——将人类可读的Java代码转化为JVM能够理解的字节码指令。这些由…...
第3篇:数据库路由模块设计与 SQL 路由策略解析
3.1 什么是数据库路由? 在分库分表或多数据库实例架构中,**数据库路由模块(SQL Router)**的作用是: 将客户端发来的 SQL 请求路由到正确的后端数据库实例或分片表中执行。 它是数据库中间件的核心组件之一。 3.2 数据…...

微软markitdown PDF/WORD/HTML文档转Markdown格式软件整合包下载
本次和大家分享另一个微软发布的非常热门的文件文档转Markdown格式文档的软件markitdown,软件可以将PDF,word,ppt,Excel等十几种格式文档转换为markdown格式文档,我基于当前最新0.1.2版本制作了免安装一键启动整合包。…...
使用source ~/.bashrc修改环境变量之后,关闭服务器,在重启,环境变量还有吗?
环境变量在服务器重启后的留存性分析 1. 环境变量的存储机制 临时环境变量: 通过命令直接设置的环境变量(如 export MY_VARvalue)仅存在于当前 shell 会话中,服务器重启后会丢失。永久环境变量: 写入 配置文件&#…...

MagicAnimate 论文解读:引入时间一致性的视频人物动画生成方法
1. 前言/动机 问题:现有动画生成方法缺乏对时间信息的建模,常常出现时间一致性差的问题 描述: 现有的动画生成方法通常采用帧变形(frame-warping)技术,将参考图像变形以匹配目标动作。尽管这类方法能生成较…...
Spring Boot 基础知识全面解析:快速构建企业级应用的核心指南
一、Spring Boot 概述:重新定义 Java 开发 1.1 什么是 Spring Boot? Spring Boot 是基于 Spring 框架的快速开发框架,旨在简化 Spring 应用的初始搭建及开发过程。它通过 「约定优于配置」(Convention Over Configuration&#…...

clickhouse如何查看操作记录,从日志来查看写入是否成功
背景 插入表数据后,因为原本表中就有数据,一时间没想到怎么查看插入是否成功,因为对数据源没有很多的了解,这时候就想怎么查看下插入是否成功呢,于是就有了以下方法 具体方法 根据操作类型查找,比如inse…...
MySQ-8.42 MGR 组复制部署及详解
目录 1 MGR要求 2 操作系统信息和软件版本 3 集群架构图 4 MySQL MGR 主库部署步骤 1 MGR要求 InnoDB 存储引擎 表上必须存在主键或唯一非空索引 MGR可允许的最大节点9个 2 操作系统信息和软件版本 rootu24-mysql-mgr-42:~# cat /etc/issue Ubuntu 24.04.2 LTS \n \l mysql…...