为什么是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数据库的连接池?的更多相关文章

  1. MySQL数据库的连接池问题

    3. sqlalchemy设置连接池数量上限设置 SQLALCHEMY_POOL_SIZE = 100 SQLALCHEMY_MAX_OVERFLOW = 0 # 超出连接池数量的连接后,最多可以连接 ...

  2. MySQL数据库远程连接

    12.00 MySQL数据库远程连接 参考: http://www.jb51.net/article/24508.htm http://www.linuxdiyf.com/viewarticle.ph ...

  3. Swift3.0服务端开发(四) MySQL数据库的连接与操作

    本篇博客我们来聊聊MySQL数据库的连接与操作.如果你本地没有MySQL数据库的话,需要你先安装MySQL数据库.在Mac OS中使用brew包管理器进行MySQL的安装是及其方便的.安装MySQL的 ...

  4. Java对MySQL数据库进行连接、查询和修改(转)

    Java对MySQL数据库进行连接.查询和修改 0. 一般过程: (1) 调用Class.forName()方法加载驱动程序. (2) 调用DriverManager对象的getConnection( ...

  5. Web框架之Django-20-基于mysql数据库的连接

    Web框架之Django-20-基于mysql数据库的连接   想要连接mysql首先需要安装pymysql这个驱动     然后在app的init文件中引入驱动 import pymysql pym ...

  6. MySql数据库之连接查询

    在MySql数据库中连接查询分为以下几种方式: 1.内连接查询 内连接查询通过关键字 inner join 关键字来实现,通过代码实现: select * from 表1 inner join 表2 ...

  7. java与MySQL数据库的连接

    java与MySQL数据库的连接 1.数据库的安装和建立参见上一篇博客中的第1,2步骤.(http://blog.csdn.net/nuptboyzhb/article/details/8043091 ...

  8. Python与Mysql 数据库的连接,以及查询。

    python与mysql数据库的连接: pymysql是python中对数据库的连接模块:因此应当首先安装pymysql数据库模块. 执行pip install pymysql 命令. 然后在pyth ...

  9. 解决Mysql连接池被关闭 ,hibernate尝试连接不能连接的问题。 (默认mysql连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池。系统发布第二天访问链接关闭问题。

    解决Mysql连接池被关闭  ,hibernate尝试连接不能连接的问题. (默认MySQL连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池. 所以系统发布第二天访问会 ...

随机推荐

  1. 【NX二次开发】Block UI 切换开关

    属性说明 常规         类型 描述     BlockID     String 控件ID     Enable     Logical 是否可操作     Group     Logical ...

  2. 生成树协议(STP)

    一.交换网络环路的产生 1.广播风暴的形成 2.多帧复制 3.MAC地址表紊乱 二.STP简介 STP-Spanning Tree Protocol(生成树协议) 逻辑上断开环路,防止广播风暴的产生 ...

  3. 安装Apache、Nginx和PHP-基于Centos7环境

    使用的软件:putty或Xshell都可. 一.搭建Apache 1.编译安装 (1).安装编译器 yum install -y gcc (2)安装Opensll 查询官网得到OpenSSL下载网址h ...

  4. 本地SQL Server怎么连接服务器上的数据库

    在阿里云的安全组规则中的入方向加上端口1433就好了,首先得要有这个,其他的都是后话

  5. 一文带你了解.Net信号量

    本文主要讲解.Net基于Semaphore带大家了解信号量 信号量举例 大家去银行去银行取钱,互斥锁管理的时一个柜台是否正在处理业务,而信号量管理的是整个柜台是否正在处理业务,每当有一个柜台处理完成之 ...

  6. vsftp安装错误总结

    1.vsftpd 530 Login incorrect 解决办法:将用户从/etc/vsftpd/ftpusers  中删除 参考:http://blog.51yip.com/linux/1672. ...

  7. 【转载】每天一个linux命令(11):nl命令

    转载至:http://www.cnblogs.com/peida/archive/2012/11/01/2749048.html nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内 ...

  8. Fiddler大全之断点操作

    打断点(bpu) 1.为什么要打断点呢?比如一个购买的金额输入框,输入框前端做了限制100-1000,那么我们测试的时候,需要测试小于100的情况下.很显然前端只能输入大于100的.这是我们可以先抓到 ...

  9. ZYNQ 中PS端GPIO EMIO使用

    ZYNQ 中PS端GPIO EMIO使用 在使用ZYNQ进行开发设计时,往往需要对一些GPIO引脚进行配置,传统的配置方法通常在PL端进行管脚约束之后在Verilog代码中对相应引脚进行配置.这样如果 ...

  10. NSIS 插件开发引发的思考

    支持NSIS的DLL扩展编程通用语法结构 #include <windows.h> #include <stdio.h> #define FORCE_SWITCH " ...