为什么是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. 基于Android平台的图书管理系统的制作(2)

    上一篇讲解了制作图书管理系统的初衷与要求,和app首页的代码. 下面来介绍图书管理系统的服务对象:学生 学生类的设计: 个人信息:账号.密码.姓名.学号.邮箱.年龄. 借阅信息:借阅总数(不超过十本) ...

  2. 为什么选择ASP.NET Core

    什么是.NET 有一次小飞去面试,面试官上来就问了一个宏观的问题:"你觉得什么是.NET"?小飞的脑子嗡嗡的,支吾了半天,才吐了一些碎片化的词语:"跨平台.开源.微软-& ...

  3. 『言善信』Fiddler工具 — 17、Fiddler常用插件(Willow)

    目录 1.Traific Difer插件 2.PDF View插件 3.JavaScript Formatter插件 4.CertMaker for iOS and Android插件 5.Synta ...

  4. ClickHouse学习系列之七【系统命令介绍】

    背景  前面介绍了ClickHouse相关的系列文章,该系列文章包括了安装.权限管理.副本分片.配置说明等.这次介绍一些ClickHouse相关的系统命令,如重载配置文件.关闭服务和进程.停止和启动后 ...

  5. Redisson 分布式锁源码 02:看门狗

    前言 说起 Redisson,比较耳熟能详的就是这个看门狗(Watchdog)机制. 本文就一起看看加锁成功之后的看门狗(Watchdog)是如何实现的? 加锁成功 在前一篇文章中介绍了可重入锁加锁的 ...

  6. 什么是CAP?

    1. 什么是CAP 是一种定理,多用于描述分布式架构,CAP这三个字母对应三种理念,且这三种理念只能两两组合,不能CAP三种理念同时共存(为什么?下面说). C:Consisteny(一致性) A:A ...

  7. USB上位机通信:CyAPI

    至今的工作中,有USB接口通信的需求,记录一下. 建立一个USB设备对象 CCyUSBDevice *USBDevice = new CCyUSBDev(Handle): 打开USB设备 一个USB设 ...

  8. 97、配置yum源仓库服务器

    (服务端(双(外,内)网卡)--客户端(内网)) YUM主要用于自动安装.升级rpm软件包,它能自动查找并解决rpm包之间的依赖关系.要成功的使用YUM工具安装更新软件或系统, 就需要有一个包含各种r ...

  9. 面试官:spring中定义bean的方法有哪些?我一口气说出了12种,把面试官整懵了。

    前言 在庞大的java体系中,spring有着举足轻重的地位,它给每位开发者带来了极大的便利和惊喜.我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工 ...

  10. SpringCloud:扩展zuul配置路由访问

    继续上次整合SpringCloud的demo进行扩展zuul:https://www.cnblogs.com/nhdlb/p/12555968.html  这里我把zuul划分出一个模块单独启动 创建 ...