为什么是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. Unicode编码转换, MD5加密,URL16进制加密解密

    一.站长网址:http://www.msxindl.com/ 1.Unicode与中文互转 16进制Unicode编码转换.还原   :http://www.msxindl.com/tools/uni ...

  2. asp.net core 实现 face recognition 使用 tensorflowjs(源代码)

    功能描述 上传照片文件名及是系统要识别标签或是照片的名称(人物标识) 提取照片脸部特征值(调用 facemesh模型) 保存特征值添加样本(调用 knnClassifier) 测试上传的图片是否识别正 ...

  3. 从HTTP到HTTPS

    从HTTP到HTTPS HTTP存在的缺陷 通信使用明文(不加密),内容可能会被窃听 不验证通信方的身份,因此有可能遭遇伪装 无法证明报文的完整性,所以有可能已遭篡改 防窃听 通信加密 HTTP 协议 ...

  4. 一次性讲清楚spring中bean的生命周期之二:FactoryBean的前世今生

    前言 在<spring中FactoryBean是什么bean>一文中,带着小伙伴学习了spring中的FactoryBean,了解了到了FactoryBean其实是一种生产Bean的bea ...

  5. Vue(12)组件的组织结构和组件注册

    组件的组织 通常一个应用会以一棵嵌套的组件树的形式来组织: 例如,你可能会有页头.侧边栏.内容区等组件,每个组件又包含了其它的像导航链接.博文之类的组件. 为了能在模板中使用,这些组件必须先注册以便 ...

  6. 4.13、nfs挂载优化及优缺点

    1.硬盘:sas/ssd磁盘,买多块,硬件raid5/raid0,网卡吞吐量要大,至少千兆(多网卡bond0) 2.nfs客户端挂载说明: 文件系统有自己的权限,挂载是建立在文件系统之上的,然后更改挂 ...

  7. 关于 Index '8' specified is out of bounds.

    报类似这样的错误暂时我只发现了两个原因: 1, 数组超出了界线,这个自己多多注意,加判断,在循环的时候看看是不是有结束条件 2, 你需要提交的网页不存在.有可能是因为你没有这个文件.可能是你的文件名错 ...

  8. java.lang.Excetion,java.lang.RuntimeException,java.lang.Error有什么区别?

    Error类对象由Java虚拟机生成并抛出,Exception类对象由应用程序处理或抛出 Error表示恢复不是不可能但很困难的情况下的一种严重问题.比如说内存溢出.不可能指望程序能处理这样的情况. ...

  9. SpringBoot中如何监听两个不同源的RabbitMQ消息队列

    spring-boot如何配置监听两个不同的RabbitMQ 由于前段时间在公司开发过程中碰到了一个问题,需要同时监听两个不同的rabbitMq,但是之前没有同时监听两个RabbitMq的情况,因此在 ...

  10. python字典和列表使用

    一.字典中健值为列表或字典 1 a.setdefault(key,[]).append(b)--键值是列表 2 a.setdefault(key,{}).append(b)--键值是字典 二.键值为列 ...