本文主要介绍基于MySQL、使用C++语言实现数据库连接池的方法。

1. 准备依赖的文件

我们利用 MySQL 提供的“mysql-connector-c++”以及 boost 编写数据库连接池。

1.1 mysql-connector-c++安装

“mysql-connector-c++”可以从 MySQL 的官网上下载,如下图:

说明:

  • 上图中的链接为(https://dev.mysql.com/downloads/connector/cpp/),此链接后续可能会改变;
  • 需要根据实际情况(如 OS 及其版本)选择对应的发行版;

在上述页面中下载下来的文件,直接包含了“mysql-connector”的头文件和共享库,如下:

注意:“mysql-connector”提供的头文件“mysql_connection.h”放在了“jdbc”目录下(如上图),本文虽然使用的是C++语言,但是也需要连接该头文件。

1.2 boost安装

使用 yum 直接安装,如下:

yum install boost-devel.x86_64

2. 示例代码

数据库连接池头文件(connection_pool.h),代码如下:

#ifndef __CONNECTION_POOL_H__
#define __CONNECTION_POOL_H__#include <mysql_connection.h>
#include <mysql_driver.h>
#include <cppconn/exception.h>
#include <cppconn/driver.h>
#include <cppconn/connection.h>
#include <cppconn/resultset.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/statement.h>
#include <pthread.h>
#include <list>
#include <string>using namespace std;
using namespace sql;class ConnPool
{
public:~ConnPool();// 获取数据库连接Connection* GetConnection();// 将数据库连接放回到连接池的容器中void ReleaseConnection(Connection *conn);// 获取数据库连接池对象static ConnPool* GetInstance();private:// 当前已建立的数据库连接数量int curSize;// 连接池定义的最大数据库连接数int maxSize;string username;string password;string url;// 连接池容器list<Connection*> connList;// 线程锁pthread_mutex_t lock;static ConnPool* connPool;Driver* driver;// 创建一个连接Connection* CreateConnection();// 初始化数据库连接池void InitConnection(int iInitialSize);// 销毁数据库连接对象void DestoryConnection(Connection *conn);// 销毁数据库连接池void DestoryConnPool();// 构造方法ConnPool(string url, string user,string password, int maxSize);
};#endif/*_CONNECTION_POOL_H */

数据库连接池实现文件(connection_pool.cpp),代码如下:

#include <stdexcept>
#include <exception>
#include <stdio.h>
#include "connection_pool.h"using namespace std;
using namespace sql;ConnPool* ConnPool::connPool = NULL;// 获取连接池对象,单例模式
ConnPool* ConnPool::GetInstance()
{if (connPool == NULL){connPool = new ConnPool("tcp://192.168.213.130:3306", "root", "", 20);}return connPool;
}// 数据库连接池的构造函数
ConnPool::ConnPool(string url, string userName, string password, int maxSize)
{this->maxSize = maxSize;this->curSize = 0;this->username = userName;this->password = password;this->url = url;try{this->driver = sql::mysql::get_driver_instance();}catch (sql::SQLException& e){perror("get driver error.\n");}catch (std::runtime_error& e){perror("[ConnPool] run time error.\n");}// 在初始化连接池时,建立一定数量的数据库连接this->InitConnection(maxSize/2);
}// 初始化数据库连接池,创建最大连接数一半的连接数量
void ConnPool::InitConnection(int iInitialSize)
{Connection* conn;pthread_mutex_lock(&lock);for (int i = 0; i < iInitialSize; i++){conn = this->CreateConnection();if (conn){connList.push_back(conn);++(this->curSize);}else{perror("Init connection error.");}}pthread_mutex_unlock(&lock);
}// 创建并返回一个连接
Connection* ConnPool::CreateConnection()
{Connection* conn;try{// 建立连接conn = driver->connect(this->url, this->username, this->password);return conn;}catch (sql::SQLException& e){perror("create connection error.");return NULL;}catch (std::runtime_error& e){perror("[CreateConnection] run time error.");return NULL;}
}// 从连接池中获得一个连接
Connection* ConnPool::GetConnection()
{Connection* con;pthread_mutex_lock(&lock);// 连接池容器中还有连接if (connList.size() > 0){// 获取第一个连接con = connList.front();// 移除第一个连接connList.pop_front();// 判断获取到的连接的可用性// 如果连接已经被关闭,删除后重新建立一个if (con->isClosed()){delete con;con = this->CreateConnection();// 如果连接为空,说明创建连接出错if (con == NULL){// 从容器中去掉这个空连接--curSize;}}pthread_mutex_unlock(&lock);return con;}// 连接池容器中没有连接else{// 当前已创建的连接数小于最大连接数,则创建新的连接if (curSize < maxSize){con = this->CreateConnection();if (con){++curSize;pthread_mutex_unlock(&lock);return con;}else{pthread_mutex_unlock(&lock);return NULL;}}// 当前建立的连接数已经达到最大连接数else{perror("[GetConnection] connections reach the max number.");pthread_mutex_unlock(&lock);return NULL;}}
}// 释放数据库连接,将该连接放回到连接池中
void ConnPool::ReleaseConnection(sql::Connection* conn)
{if (conn){pthread_mutex_lock(&lock);connList.push_back(conn);pthread_mutex_unlock(&lock);}
}// 数据库连接池的析构函数
ConnPool::~ConnPool()
{this->DestoryConnPool();
}// 销毁连接池,需要先销毁连接池的中连接
void ConnPool::DestoryConnPool()
{list<Connection*>::iterator itCon;pthread_mutex_lock(&lock);for (itCon = connList.begin(); itCon != connList.end(); ++itCon){// 销毁连接池中的连接this->DestoryConnection(*itCon);}curSize = 0;// 清空连接池中的连接connList.clear();pthread_mutex_unlock(&lock);
}// 销毁数据库连接
void ConnPool::DestoryConnection(Connection* conn)
{if (conn){try{// 关闭连接conn->close();}catch(sql::SQLException& e){perror(e.what());}catch(std::exception& e){perror(e.what());}// 删除连接delete conn;}
}

数据库连接池测试代码(test_dbpool.cpp):

#include "connection_pool.h"//初始化连接池
ConnPool *connpool = ConnPool::GetInstance();int main(int argc, char* argv[])
{Connection *con;Statement *state;ResultSet *result;// 从连接池中获取连接con = connpool->GetConnection();for (int i = 0; i < 100; i++){state = con->createStatement();state->execute("use testdb");// 查询数据库result = state->executeQuery("select * from roles");// 打印数据库查询结果cout << "================================" << endl;while (result->next()){int nRoleId = result->getInt("role_id");string strOccupation = result->getString("occupation");string strCamp = result->getString("camp");cout << nRoleId << " , " << strOccupation << " , " << strCamp << endl;}cout << "i is: " << i << endl;cout << "================================" << endl;}delete result;delete state;connpool->ReleaseConnection(con);return 0;
}

编译上述文件,生成数据库连接池测试程序,如下:

g++ -o test_dbpool test_dbpool.cpp connection_pool.cpp -I /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/include/jdbc/ -L /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/lib64/ -lmysqlcppconn

执行上面生成的测试程序,命令如下:

./test_dbpool

程序执行(部分)结果如下:

上述结果表明我们编写的数据库连接池正常运行了。

3. 总结

这里对数据库连接池的实现代码进行简单地总结。

3.1 存放空闲连接的容器

数据库连接池头文件(connection_pool.h)中定义了一个list容器 connList ,使用 connList 存放空闲的连接。

3.2 线程锁

在对 connList 内的连接进行操作的时候,需要通过加锁来保证程序的安全性,所以头文件中定义了一个线程锁 lock ,通过使用 lock 保证同一时间只能有一个线程对容器 connList 进行操作。

3.3 单例模式

由于连接池类 ConnPool 要统一管理系统中的所有连接,所以在整个系统中只需要维护一个连接池对象。如果系统中定义了多个连接池对象,那么每一个对象都可以建立 maxSize 个连接,在这种情况下使用连接池没有任何意义(与不使用连接池效果一样),也破环了通过连接池统一管理系统中连接的初衷,所以数据库连接池需要使用单例模式编写连接池类:单例模式确保一个类只有一个实例,该类自己进行实例化,并且向整个系统提供这个实例。

在头文件 connection_pool.h 中,我们定义了一个静态的连接池对象 connPool ,连接池类 ConnPool 提供一个静态的公共方法 GetInstance() ,外部程序通过调用这个方法来获得连接池对象。由于把连接池类 ConnPool 的构造函数定义为私有的,所以外部的应用程序不能够通过 new 的方式来实例化连接池类,只能通过 GetInstance() 方法获得连接池对象。在 GetInstance() 方法中,需要判断连接池类中定义的连接池对象 connPool 是否为“NULL”,若为“NULL”则调用私有构造函数实例化 connPool ;若不为“NULL”,则直接返回 connPool ,这样就实现了连接池类的单例模式,从而保证了系统运行过程中只建立一个连接池类的实例对象。

3.4 连接池的初始化

在实例化连接池类的对象 connPool 时,要对连接池做一些初始化操作,即建立一定数量的数据库连接。本文的程序通过 void InitConnection(int iInitialSize) 方法对连接池进行初始化,创建 iInitialSize 个连接,并且将这些连接放到连接池中的容器 connList 中,每新建一个连接,当前已建立的连接数 curSize 就加1。

3.5 从连接池中获取连接

当程序进行访问数据库时,需要从连接池中获取一个连接,本文是通过 GetConnection() 方法实现的,大致步骤如下:

1. 首先判断容器中是否还有空闲连接,如果有,则取出容器中的第一个连接,并且将该连接从容器中移除;

1.1 判断上一步中获得的连接是否已经关闭,如果已关闭,则回收该连接的内存空间,并且重新创建一个连接,然后判断新创建的连接是否为空,如果为空,则需要将已经建立连接的数量减1,去掉这个空连接(也可以说是前面已经关闭的连接)。

2. 如果容器中已经没有空闲连接了,则要判断当前的 curSize 值是否已经达到规定的最大连接数 maxSize ;

2.1 如果小于 maxSize ,就建立一个新的连接,同时 ++curSize

2.2 如果不小于 maxSize ,则等待其他数据库访问请求释放数据库连接。

3.6 将连接放回到连接池中

连接使用完后,需要将该连接放回连接池中,本文通过 void ReleaseConnection(Connection *conn) 方法实现。该方法的具体实现就是将传进来的数据库连接(重新)添加到连接池的容器 connList 中。

3.7 销毁连接池

当程序需要销毁数据库连接池(回收连接池的内存空间)时,需要先关闭、销毁连接池中的连接(回收连接池中所有连接的内存空间),然后再释放连接池对象的内存空间(即容器的 clear 操作)。

数据库连接池的实现方法(MySQL+C++)相关推荐

  1. java连接mysql数据库连接池_java使用原生MySQL实现数据的增删改查以及数据库连接池技术...

    一.工具类及配置文件准备工作 1.1 引入jar包 使用原生MySQL,只需要用到MySQL连接的jar包,maven引用方式如下: mysql mysql-connector-java 5.1.48 ...

  2. Tomcat数据库连接池的配置方法总结(叶涛为您解答)

    实例使用的Tomcat版本为6.0 方法一: 在Tomcat的conf/context.xml中配置在Tomcat\apache-tomcat-6.0.33\conf目录下的context.xml文件 ...

  3. Node.js SQL数据库操作 (上)(操作MySQL数据库及 数据库连接池)

    文章目录 Node.js MySQL驱动 操作 MySQL 数据库 连接 MySQL 数据库 增删改查操作 防止 SQL 注入攻击 数据库连接池操作 Node.js MySQL驱动 Node.js的原 ...

  4. Python操作数据库及Python实现mysql数据库连接池源代码

    简介 pymysql:纯Python实现的一个驱动.因为是纯Python编写的,因此执行效率不如MySQL-python.并且也因为是纯Python编写的,因此可以和Python代码无缝衔接. MyS ...

  5. python 数据库连接池_【转】Python 数据库连接池

    python编程中可以使用pymysql进行数据库连接及增删改查操作,但每次连接mysql请求时,都是独立的去请求访问,比较浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响.因 ...

  6. c3p0,DBCP,Druid(德鲁伊)数据库连接池

    c3p0,DBCP,Druid(德鲁伊)数据库连接池 每博一文案 佛说:前世 500 次的回眸,才换来今生的一次擦肩而过. 人与人之间的缘分,真的无需强求,并不是所有的感情都能天长地久,正如<越 ...

  7. Spring——配置数据源和数据库连接池

    文章目录 一.高内聚低耦合 二.数据库连接池   1.什么是数据库连接池   2.常用数据库连接池     Druid 二.配置数据源   1.抽取properties文件   2.spring配置数 ...

  8. Python 数据库连接池

    python编程中可以使用pymysql进行数据库连接及增删改查操作,但每次连接mysql请求时,都是独立的去请求访问,比较浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响.因 ...

  9. 数据库连接池DBPool分析(一):简介

    2019独角兽企业重金招聘Python工程师标准>>> 刚刚毕业的本科生,在研究了公司的框架之后,自己花了三天的时间用C++实现了简单的数据库连接池,包括了Mysql.正在加入Red ...

  10. 数据库连接池(Druid)

    数据库连接池 ①没有使用数据库连接池之前 当有多个线程,每个线程都需要连接mysql数据库,执行SQL语句,那么每一个线程都会创建一个Connection连接,当操作完毕之后也会关闭这个连接.这样频繁 ...

最新文章

  1. mysql 去掉复合索引_MySQL性能优化[实践篇]-复合索引实例
  2. 图片上传之后清空_OSS文件上传及OSS与ODPS之间数据连通
  3. Django从理论到实战(part49)--ModelForm
  4. 再谈angularjs DI(Dependency Injection)
  5. 《LeetcodeHot100非困难题补录》
  6. JS编程建议——52:建议使用splice删除数组
  7. kafka0.9 java commit_Kafka 0.9 新消费者API
  8. 【SQL】IN、EXISTS和表连接三者的效率比较
  9. VUE 下载文件流 文件无法打开,缺失数据
  10. C语言打印九九口诀表
  11. 利用HttpClient4,实现get,post 参数,post json,post file
  12. 光伏发电matlab模块,光伏发电的matlab仿真.docx
  13. iPhone屏幕分辨率
  14. C语言 母牛生小牛问题 多组测试数据
  15. USB core(一) - rh_queue_status与rh_call_control
  16. hiphop 2.1 开发问题总结 原创-胡志广
  17. 【Android源码面试宝典】MMKV从使用到原理分析(一)
  18. Lucas Kanade 光流法(来自wiki 百科)
  19. linux怎么看系统内存多大内存频率,linux 系统管理中的查看内存插槽数最大容量和频率...
  20. L2-040 哲哲打游戏 (25 分)(模拟)

热门文章

  1. 何为计算机视觉?计算机视觉与数字图像处理的区别、Opencv的起源。
  2. body-parser和multer
  3. css02基本选择器
  4. FFmpeg 在ubuntu 中编译出so
  5. Netty框架中的@Skip使用说明
  6. 【原创】C#实现视频远程监控(下载)
  7. 诸如北京现代 只有四个轮子和一电瓶由中国制造(转自新华网)
  8. 谁说80后的人不负责任!
  9. 17.看板方法——瓶颈和非即时可用资源笔记
  10. java byte数组与String互转