如何用C++自己实现mysql数据库的连接池?
为什么是mysql?
现在几乎所有的后台应用都要用到数据库,什么关系型的、非关系型的;正当关系的,不正当关系的;主流的和非主流的, 大到Oracle,小到sqlite,以及包括现在逐渐流行的基于物联网的时序数据库,比如涛思的TDengine,咱们中国人自己的开源时序数据库,性能杠杠滴。
凡此总总,即使没用过,也听说过,但大部分人或企业用的最多的就是白嫖型数据库:mysql。该数据库的特点就是无论是个人还是企业都能玩的起。像Oracle这种名媛型数据库基本就属于银行特供,银行需要花钱买平安,心里踏实。不买对的,只选贵的,因为人家确实不差钱。
如果你的后台应用连数据库都不需要,那跟咸鱼网站有什么区别呢?就是咸鱼二手网也要用到数据库的。如果一个IT民工一辈子没用过数据库就在35(~45)岁时“被退休”,那他的职业生涯是遗憾的,是不完美的,是不纯粹的。 好歹年轻是也要用一下非主流的Access吧,哪怕Execel也成。这种感觉就好比在大学时没谈过恋爱一样,光忙着羡慕别人就突然毕业了。
为什么要搞资源池?
目前大部分后台程序都选择Java开发或PHP,这两种语言的第三方库非常丰富,丰富到让开发人员的只要将精力放在具体业务上即可。比如数据库的资源池,只要选择好适当的jar包外加配置好相应的数据库参数,即可放心大胆的使用mysql。
当然,如果你命硬的话,也可以选择用C或C++开发后台应用。这时候你就需要自己DIY一个数据库资源池。
如果只是一个客户端程序,基本不需要连接池,但对于后台应用来说,高并发就意味着多线程,多线程程就意味着资源的竞争。内存访问如此,数据库访问也是如此。每次数据库的打开和关闭就是一次网络连接和关闭的过程,频繁的打开和关闭无疑会浪费大量的系统资源。这时候就需要提前建立好N个连接,并放在资源池中并提供给不同线程访问使用。
mysql资源池实现的案例源码
我一直相信好的代码是不需要过的语言来解释的,代码即文档,要啥自行车。以下案例只是一个实现思路,供参考。
头文件:MysqlPool.h
#pragma warning(disable : 4786)	
#include <windows.h>
#include <winsock2.h>
#include <mysql.h>					// 确保你的机器有mysql开发库
#include <vector>
#include <string>
using namespace std;
#define DEFAULT_POOL_SIZE		20	// 缺省mysql连接池中的数量
#define DEFAULT_POOL_TIMEOUT	60	// 获取池中mysql连接的超时
// 自定义数据库查询回调函数
typedef BOOL (CALLBACK *LPFN_RetrieveRecordData)(MYSQL_ROW& sqlRow, MYSQL_FIELD* pSqlFields, int iFieldCount, DWORD dwUserData);
// Mysql数据库连接类
class CMysqlConn
{
public:
	CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName,
				const char* pszDBUser, const char* pszDBPwd);
	virtual ~CMysqlConn();
public:
	// 打开/关闭一个mysql连接
	BOOL Open();
	void Close();
	// ping连接是否已关闭
	BOOL Ping();
	// 重置字符集
	BOOL ResetCharset();
public:
	// ================SQL语句操作(简单实现几个)================
	// 查询
	BOOL	Select(const char* pszSql, LPFN_RetrieveRecordData lpfnRetrieveRecordData, DWORD dwUserData);
	// 执行
	BOOL	Execute(const char* pszSql);
	// 插入,如果主键是自增整型,返回插入后的主键值
	__int64 Insert(const char* pszSql);
protected:
	MYSQL*	m_pMysql;			// mysql数据库操作对象
	// 以下是连接mysql需要的参数
	string	m_strDBServer;		// mysql数据库所在服务器
	UINT	m_uDBPort;			// mysql数据库连接端口
	string	m_strDBName;		// 数据库名称
	string	m_strDBUser;		// 数据库账户
	string	m_strDBPwd;			// 数据库密码
};
// 数据库连接池实现
class CMysqlPool
{
public:
	CMysqlPool();
	virtual ~CMysqlPool();
	// 创建mysql连接池
	BOOL Create(const char* pszDBServer, UINT uDBPort, const char* pszDBName,
				const char* pszDBUser, const char* pszDBPwd,
	 			DWORD dwPoolSize = DEFAULT_POOL_SIZE,
				DWORD dwTimeOut = DEFAULT_POOL_TIMEOUT);
	// 销毁连接池
 	void Destroy();
public:
	// 获取一个mysql连接
 	CMysqlConn* Get();
	// 释放一个mysql连接
 	void Release(CMysqlConn* pConn);
protected:
	HANDLE				m_hSemaphore;       // 信号量句柄
	DWORD				m_dwPoolSize;		// 连接池大小
	DWORD				m_dwTimeOut;		// 超时,单位秒
	CRITICAL_SECTION	m_csPool;			// 连接池锁
	vector<CMysqlConn*>	m_vecIdle;			// 闲队列
	vector<CMysqlConn*>	m_vecBusy;			// 忙队列
};
实现文件:MysqlPool.cpp
#include "stdafx.h"
#include "MysqlPool.h"
#include <assert.h>
#include <algorithm>
#pragma comment(lib, "libmysql.lib")	//连接MysQL需要的库
//////////////////////////////////////////////////////////////////////
// CMysqlConn: mysql数据库连接类
//////////////////////////////////////////////////////////////////////
CMysqlConn::CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName,
						const char* pszDBUser, const char* pszDBPwd)
{
	assert(pszDBServer);
	assert(pszDBName);
	assert(pszDBUser);
	assert(pszDBPwd);
	m_pMysql = NULL;
	m_strDBServer = pszDBServer;
	m_uDBPort = uDBPort;
	m_strDBName = pszDBName;
	m_strDBUser = pszDBUser;
	m_strDBPwd = pszDBPwd;
}
CMysqlConn::~CMysqlConn()
{
	Close();
}
// 打开一个mysql数据库,即建立一个数据库连接
BOOL CMysqlConn::Open()
{
	if(m_pMysql)
	{
		mysql_close(m_pMysql);	// 关闭连接
		m_pMysql = NULL;
	}
	m_pMysql = mysql_init(NULL);
	if(!m_pMysql)
		return FALSE;
	// 连接数据库
    if(!mysql_real_connect(m_pMysql, m_strDBServer.c_str(), m_strDBUser.c_str(),
							m_strDBPwd.c_str(), m_strDBName.c_str(), m_uDBPort, NULL, 0))
    {
		int i = mysql_errno(m_pMysql);
		const char * pszErr = mysql_error(m_pMysql);
		return FALSE;
	}
	// 设置重连
	char chValue = 1;
	mysql_options(m_pMysql, MYSQL_OPT_RECONNECT, &chValue);
	mysql_query(m_pMysql,"set names 'gbk'"); 
	return TRUE;
}
// 关闭数据库连接
void CMysqlConn::Close()
{
	if(m_pMysql)
		mysql_close(m_pMysql);	// 断开连接
	m_pMysql = NULL;
}
// ping一下mysql,看看连接还活着
BOOL CMysqlConn::Ping()
{
	if(m_pMysql)
		return (0 == mysql_ping(m_pMysql));
	return FALSE;
}
// 设置字符集为GBK
BOOL CMysqlConn::ResetCharset()
{
	if(m_pMysql)
		return (0 == mysql_query(m_pMysql, "set names 'gbk'"));
	return FALSE;
}
// mysql执行:delete 或 update
BOOL CMysqlConn::Execute(const char* pszSql)
{
	assert(pszSql);
	if(!m_pMysql)
		return FALSE;
	MYSQL_STMT *myStmt = mysql_stmt_init(m_pMysql);
	if(!myStmt)
	{
		return FALSE;
	}
	if(0 != mysql_stmt_prepare(myStmt, pszSql, strlen(pszSql)))
	{
		mysql_stmt_close(myStmt);
		return FALSE;
	}
	if(0 != mysql_stmt_execute(myStmt))
	{
		mysql_stmt_close(myStmt);
		return FALSE;
	}
	mysql_stmt_close(myStmt);
	return TRUE;
}
// mysql插入
__int64 CMysqlConn::Insert(const char* pszSql)
{
	assert(pszSql);
	MYSQL_STMT *myStmt = mysql_stmt_init(m_pMysql);
	if(!myStmt)
		return 0;
	if(0 != mysql_stmt_prepare(myStmt, pszSql, strlen(pszSql)))
	{
		int i = mysql_errno(m_pMysql);
		const char * s = mysql_error(m_pMysql);
		mysql_stmt_close(myStmt);
		return 0;
	}
	if(0 != mysql_stmt_execute(myStmt))
	{
		mysql_stmt_close(myStmt);
		return 0;
	}
	mysql_stmt_close(myStmt);
	__int64 i64ID = mysql_insert_id(m_pMysql);
	return i64ID;
}
// mysql查询
BOOL CMysqlConn::Select(const char* pszSql, LPFN_RetrieveRecordData lpfnRetrieveRecordData, DWORD dwUserData)
{
	if(!m_pMysql)
		return FALSE;
	if(NULL == lpfnRetrieveRecordData)
		return FALSE;
	if(0 != mysql_real_query(m_pMysql, pszSql, strlen(pszSql)))
	{
		return FALSE;
	}
	MYSQL_RES *resRecord = mysql_store_result(m_pMysql);
	int iFieldCount = resRecord->field_count;
	MYSQL_ROW sqlRow;
	while (sqlRow = mysql_fetch_row(resRecord))
    {
		if(!lpfnRetrieveRecordData(sqlRow, resRecord->fields, iFieldCount, dwUserData))
			break;
	}
	mysql_free_result(resRecord);
	return TRUE;
}
//////////////////////////////////////////////////////////////////////
// CMysqlPool: mysql数据库连接池类
//////////////////////////////////////////////////////////////////////
CMysqlPool::CMysqlPool()
{
	::InitializeCriticalSection(&m_csPool);
}
CMysqlPool::~CMysqlPool()
{
	Destroy();
	::DeleteCriticalSection(&m_csPool);
}
// 创建mysql连接池
BOOL CMysqlPool::Create(const char* pszDBServer, UINT uDBPort, const char* pszDBName,
						const char* pszDBUser, const char* pszDBPwd,
						DWORD dwPoolSize, DWORD dwTimeOut)
{
	m_dwTimeOut = dwTimeOut;
	m_dwPoolSize = dwPoolSize;
	// 创建信号量
	m_hSemaphore = ::CreateSemaphore(NULL, dwPoolSize, dwPoolSize, NULL);
	if (NULL == m_hSemaphore)
	{
		return FALSE;
	}
	// 创建数据库连接池
	for(DWORD i = 0; i < dwPoolSize; ++i)
	{
		// 创建一个mysql数据库连接
		CMysqlConn *pConn = new CMysqlConn(pszDBServer, uDBPort, pszDBName, pszDBUser, pszDBPwd);
		if(!pConn->Open())
		{
			delete pConn;
			continue;
		}
		m_vecIdle.push_back(pConn);
	}
	return m_vecIdle.size() > 0;
}
// 销毁mysql连接池
void CMysqlPool::Destroy()
{
	::CloseHandle(m_hSemaphore);
	m_hSemaphore = NULL;
	// 释放idle队列
	vector<CMysqlConn*>::iterator it;
    for(it = m_vecIdle.begin(); it != m_vecIdle.end(); ++it)
	{
		CMysqlConn* pConn =  *it;
		delete pConn;
	}
	m_vecIdle.clear();
	// 释放busy队列
	while(!m_vecBusy.empty())
	{
		CMysqlConn* pConn =  m_vecBusy.back();
		m_vecBusy.pop_back();
		delete pConn;
	}
}
// 从mysql连接池获取一个连接
CMysqlConn* CMysqlPool::Get()
{
	DWORD dwRet = ::WaitForSingleObject(m_hSemaphore, m_dwTimeOut*1000);
	if (WAIT_OBJECT_0 != dwRet)				// 超时,说明资源池没有可用mysql连接
	{
		printf("数据库没有可用连接。\r\n");
		return NULL;
	}
	// 从连接池中获取一个闲置连接
	CMysqlConn* pConn = NULL;
	::EnterCriticalSection(&m_csPool);
 	if (!m_vecIdle.empty())
 	{
		pConn = m_vecIdle.back();			// 移出idle队列
 		m_vecIdle.pop_back();
 		m_vecBusy.push_back(pConn);			// 加入busy队列
 	}
	::LeaveCriticalSection(&m_csPool);
	if(NULL == pConn)
		return NULL;
	// 如果一个连接长时间无通信,可能被防火墙关闭,此时可以通过mysql_ping函数测试一下
	// 本例中通过重新设置字符集
	// 重新设置字符集,并判断数据库连接是否已断开
	if(!pConn->ResetCharset())
	{
		if(!pConn->Open())
			return NULL;
	}
	printf("==》资源池:记得还我哦。\r\n");
	return pConn;
}
// 释放一个连接到mysql连接池
void CMysqlPool::Release(CMysqlConn* pConn)
{
	if(NULL == pConn)
		return;
	// 释放一个信号量
	::ReleaseSemaphore(m_hSemaphore, 1, NULL); 
	::EnterCriticalSection(&m_csPool);
	// 从Busy队列中释放该连接
	vector<CMysqlConn*>::iterator it = find(m_vecBusy.begin(), m_vecBusy.end(), pConn);
	if(it != m_vecBusy.end())
	{
		printf("POOL SIZE : %d, %d\r\n", m_vecIdle.size(), m_vecBusy.size());
		m_vecBusy.erase(it);				// 移出busy队列
		m_vecIdle.push_back(pConn);			// 加入idle队列
		printf("POOL SIZE : %d, %d\r\n", m_vecIdle.size(), m_vecBusy.size());
	}
	::LeaveCriticalSection(&m_csPool);
	printf("《==资源池说:有借有还再借不难,常来玩啊。\r\n");
}
测试函数
void TestMysqlPool()
{
	// 创建mysql连接资源池
	CMysqlPool mysqlPool;
	if(!mysqlPool.Create("127.0.0.1", 3306, "information_schema", "root", "123456"))
	{
		printf("Create mysql conneticon pool failed.\r\n");
		return;
	}
	// 从资源池中获取一个连接,连接池说:记得要还哦!
	CMysqlConn* pConn = mysqlPool.Get();
	// 假装做一次数据库操作
	char* pszSQL =  "SELECT * FROM CHARACTER_SETS";
	pConn->Select(pszSQL, RetrieveRecordData, 0);
	// 将连接还给资源池并谢谢!连接池说:不客气!
	mysqlPool.Release(pConn);
	printf("Test over.\r\n");
}
输出打印

如何用C++自己实现mysql数据库的连接池?的更多相关文章
- MySQL数据库的连接池问题
		3. sqlalchemy设置连接池数量上限设置 SQLALCHEMY_POOL_SIZE = 100 SQLALCHEMY_MAX_OVERFLOW = 0 # 超出连接池数量的连接后,最多可以连接 ... 
- MySQL数据库远程连接
		12.00 MySQL数据库远程连接 参考: http://www.jb51.net/article/24508.htm http://www.linuxdiyf.com/viewarticle.ph ... 
- Swift3.0服务端开发(四) MySQL数据库的连接与操作
		本篇博客我们来聊聊MySQL数据库的连接与操作.如果你本地没有MySQL数据库的话,需要你先安装MySQL数据库.在Mac OS中使用brew包管理器进行MySQL的安装是及其方便的.安装MySQL的 ... 
- Java对MySQL数据库进行连接、查询和修改(转)
		Java对MySQL数据库进行连接.查询和修改 0. 一般过程: (1) 调用Class.forName()方法加载驱动程序. (2) 调用DriverManager对象的getConnection( ... 
- Web框架之Django-20-基于mysql数据库的连接
		Web框架之Django-20-基于mysql数据库的连接 想要连接mysql首先需要安装pymysql这个驱动 然后在app的init文件中引入驱动 import pymysql pym ... 
- MySql数据库之连接查询
		在MySql数据库中连接查询分为以下几种方式: 1.内连接查询 内连接查询通过关键字 inner join 关键字来实现,通过代码实现: select * from 表1 inner join 表2 ... 
- java与MySQL数据库的连接
		java与MySQL数据库的连接 1.数据库的安装和建立参见上一篇博客中的第1,2步骤.(http://blog.csdn.net/nuptboyzhb/article/details/8043091 ... 
- Python与Mysql 数据库的连接,以及查询。
		python与mysql数据库的连接: pymysql是python中对数据库的连接模块:因此应当首先安装pymysql数据库模块. 执行pip install pymysql 命令. 然后在pyth ... 
- 解决Mysql连接池被关闭 ,hibernate尝试连接不能连接的问题。 (默认mysql连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池。系统发布第二天访问链接关闭问题。
		解决Mysql连接池被关闭 ,hibernate尝试连接不能连接的问题. (默认MySQL连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池. 所以系统发布第二天访问会 ... 
随机推荐
- 【C++】共用体\联合体(union)
			共用体的用法与结构体差不多,只不过将关键字由struct变成了union.共用体使不同的类型变量存放到同一段内存单元中,所以共用体在同一时刻只能存储一个数据成员的值,共用体的大小等于最大成员的大小(结 ... 
- 彻底搞懂彻底搞懂事件驱动模型 - Reactor
			在高性能网络技术中,大家应该经常会看到Reactor模型.并且很多开源软件中都使用了这个模型,如:Redis.Nginx.Memcache.Netty等. 刚开始接触时可能一头雾水,这到底是个什么东东 ... 
- Nginx为什么快到根本停不下来?
			Nginx 是一个免费的,开源的,高性能的 HTTP 服务器和反向代理,以及 IMAP / POP3 代理服务器. 图片来自 Pexels Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资 ... 
- Redis面试连环问,快看看你能走到哪一步
			今天,我不自量力的面试了某大厂的java开发岗位,迎面走来一位风尘仆仆的中年男子,手里拿着屏幕还亮着的mac,他冲着我礼貌的笑了笑,然后说了句"不好意思,让你久等了",然后示意我坐 ... 
- ThreadPoolExecutor参数详解
			ThreadPoolExecutor全部参数的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long ke ... 
- 【题解】localmaxima 数论
			# T749 localmaxima 权限限制没有超链接 题目描述 Description 给出一个排列,若其中一个数比它前面的数都大,则称为localmaxima数,求一个随机排列中localmax ... 
- noip2013 总结
			转圈游戏 题目 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,-- ... 
- Linux常用命令详解下
			Linux常用命令详解 目录 一.Linux常用命令 1.1.查看及切换目录(pwd.cd.ls.du) 1.2.创建目录和文件(mkdir.touch.ln) 1.3.复制.删除.移动目录和文件(c ... 
- CentOS-配置JDK(压缩包)
			卸载openjdk $ rpm -qa | grep jdk 以上命令用来检查linux上是否安装openjdk,如果安装需要将其全部卸载掉,卸载命令: $ rpm -e --nodeps java- ... 
- 在Ubuntu 16.04中搭建RobotFramework环境
			1.搭建RF环境 2.安装RF相关库 3.查看RF case 4.设置环境变量 相关知识点:pip --proxy=http://xx.xx.xx.xx:xx install 包名,使用pip的-- ... 
