推荐视频:
高并发技术之数据库联接池设计与实现
linux服务器开发学习地址:
1.联接池的介绍:
1.1应用背景:
通常的应用程序就会访问到数据库,在程序访问数据库的时侯,每一次数据访问恳求都必须经过下边几个步骤:构建数据库联接,打开数据库,对数据库中的数据进行操作,关掉数据库联接。而构建数据库联接和打开数据库是一件很消耗资源而且费时的工作,假如在系统中很频繁的发生这种数据库联接,必然会影响到系统的性能,甚至会造成系统的崩溃。
1.2技术思想:
在系统初始化阶段,构建一定数目的数据库联接对象(Connection),并将其储存在联接池中定义的容器中。当有数据库访问恳求时,就从联接池中的这个容器中掏出一个联接;当容器中的联接早已用完easyphp连接数据库,但是还没有达到系统定义的最大联接数时,可以再创建一个新的联接,当当前使用的联接数达到最大联接数时,就要等待其他访问恳求将联接放回容器后才会使用。当使用完联接的时侯,必须将联接放回容器中,这样不同的数据库访问恳求就可以共享那些联接,通过重复使用这种早已构建的数据库联接,可以解决上节中说到的频繁构建联接的缺点,因而提升了系统的性能。
经过上述描述,我们可以归纳出数据库联接池的主要操作:
(1)首先构建一个数据库联接池对象
(2)初始化一定数目的数据库联接,倒入联接池对象的容器中
(3)当有数据库访问恳求时,直接从联接池的容器中得到一个联接,这儿出现三种情况:
(a)当容器中的还有联接时,则返回给数据库访问恳求者一个联接
(b)当容器中没有联接时,但是当前构建的联接数没有达到系统定义的最大联接数,则创建一个新的数据库联接。
(c)当容器中的没有联接而且当前构建的联接数达到系统定义的最大联接数,则当前访问数据库恳求就要等待其他访问恳求释放联接。
(4)当数据库访问完成后,应当将联接放回联接池的容器中。
(5)当服务停止时,须要先释放数据库联接池中的所有数据库联接,之后再释放数据库联接池对象。
2.编程实现:
头文件(connection_pool.h):
/*
*File: connection_pool.h
*Author: fengwei
*/
#ifndef _CONNECTION_POOL_H
#define _CONNECTION_POOL_H
#include
#include
#include
#include
#include
#include
#include <cppconn/prepared_ Statement .h>
#include
#include < pthread .h>
#include
using namespace std;
using namespace sql;
class ConnPool {
private:
int curSize; //当前已建立的数据库连接数量
int maxSize; //连接池中定义的最大数据库连接数
string username;
string password;
string url;
list connList; //连接池的容器队列 STL list 双向链表
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); //构造方法
public:
~ConnPool();
Connection*GetConnection(); //获得数据库连接
void ReleaseConnection(Connection *conn); //将数据库连接放回到连接池的容器中
static ConnPool *GetInstance(); //获取数据库连接池对象
};
#endif /*_CONNECTION_POOL_H */
头文件中定义了一个容器connList,上面储存了好多个未使用的联接;在对容器内的联接进行操作的时侯,须要加锁来保证程序的安全性,所以头文件中定义了一个lock,通过使用lock保证了同一时间只有一个线程对容器进行操作。
【文章福利】需要C/C++Linux服务器构架师学习资料加群812855908(资料包括C/C++,Linux,golang技术,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,解释器,DPDK,ffmpeg等)
联接池类要统一管理整个应用程序中的联接,所以在整个系统中只须要维护一个联接池对象,试想:假如系统中定义了多个联接池对象,这么每一个对象都可以构建maxSize个联接,这样就丧失了创建联接池的本意easyphp连接数据库,破环了通过联接池统一管理系统中联接的思想。所以这儿使用单例模式编撰联接池类,单例模式确保一个类只有一个实例,自己进行实例化而且向整个系统提供这个实例。
饿汉实现
为何我不讲“线程安全的饿汉实现”?由于饿汉实现原本就是线程安全的,不用加锁。为何?自己想!
class singleton
{
protected :
singleton()
{}
private :
static singleton* p;
public:
static singleton* initance();
};
singleton* singleton::p = new singleton;
singleton* singleton::initance()
{
return p;
}
在头文件中,我们定义了一个静态的联接池对象connPool,联接池类提供一个静态的公共方式GetInstance(),外部程序通过调用这个方式来获得联接池对象。而且将联接池类的构造函数定义为私有的,外部的应用程序不能否通过new来实例化联接池类,只能通过GetInstance()方式获得联接池对象;在GetInstance()方式中须要判定联接池类中定义的connPool是否为NULL,若为NULL则调用私有构造函数实例化connPool,若不为空,则直接返回connPool。这样就实现了联接池类的单例模式,因而保证了系统运行过程中只完善一个联接池类的实例对象。
在实例化联接池类的对象时,要对联接池做一些初始化的操作,即构建一定数目的数据库联接。程序中通过InitConnection(intiInitialSize)方式对联接池进行初始化,创建iInitialSize个联接,但是将这种联接置于联接池中的容器connList中,每新建一个联接,curSize就加1。当有数据库访问恳求时,须要从联接池中获取一个联接,通过GetConnection()方式实现:首先判定容器中是否还有联接,假如有,则掏出容器中的第一个联接,但是将该联接移出容器;获得的联接要进行判别,假如联接早已关掉,则回收该联接的显存空间,而且重新创建一个联接;之后判定新创建的联接是否为空,倘若为空,则说明当前早已构建联接的数目并不是curSize个,而是(curSize-1)个(应当去除这个空联接)。假如容器中早已没有联接了,则要判定当前的curSize值是否早已达到规定的maxSize,假如没有大于maxSize,将构建一个新的联接(++curSize)。若果超过maxSize则等待其他数据库访问恳求释放数据库联接。
联接使用完之后,须要将联接放回联接池中,通过ReleaseConnection(sql::Connection*conn)方式实现,它的实现十分简单,就是将传进来的connection联接添加到联接池的容器中。
当须要回收联接池的显存空间时,须要先回收联接池中所有联接的显存空间,之后再释放联接池对象的显存空间。
实现数据库联接池主要的步骤就是上述这种,具体的代码实现如下所示(connection_pool.cpp):
/*
* connection_pool.cpp
*
*/
#include
#include
#include
#include "connection_pool.h"
using namespace std;
using namespace sql;
ConnPool *ConnPool::connPool = new ConnPool("tcp://127.0.0.1:3306", "root", "123456", 50);
//连接池的构造函数
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 ("驱动连接出错;n");
} catch (std::runtime_error&e) {
perror("运行出错了n");
}
this->InitConnection(maxSize / 2);
}
//获取连接池对象,单例模式
ConnPool*ConnPool::GetInstance() {
return connPool;
}
//初始化连接池,创建最大连接数的一半连接数量
void ConnPool::InitConnection(int iInitialSize) {
Connection*conn;
pthread_mutex_lock(&lock);
for (int i = 0; i CreateConnection();
if (conn) {
connList.push_back(conn);
++(this->curSize);
} else {
perror("创建CONNECTION出错");
}
}
pthread_mutex_unlock(&lock);
}
//创建连接,返回一个Connection
Connection* ConnPool::CreateConnection() {
Connection*conn;
try {
conn = driver->connect(this->url, this->username, this->password); //建立连接
return conn;
} catch (sql::SQLException&e) {
perror("创建连接出错");
return NULL;
} catch (std::runtime_error&e) {
perror("运行时出错");
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 CreateConnection();
if (con) {
++curSize;
pthread_mutex_unlock(&lock);
return con;
} else {
pthread_mutex_unlock(&lock);
return NULL;
}
} else { //建立的连接数已经达到maxSize
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::iterator icon;
pthread_mutex_lock(&lock);
for (icon = connList.begin(); icon != connList.end(); ++icon) {
this->DestoryConnection(*icon); //销毁连接池中的连接
}
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;
}
}
main.cpp
/*
* main.cpp
*
*/
#include "connection_pool.h"
namespace ConnectMySQL {
//初始化连接池
ConnPool *connpool = ConnPool::GetInstance();
void run() {
Connection *con;
Statement *state;
ResultSet *result;
// 从连接池中获取mysql连接
con = connpool->GetConnection();
state = con->createStatement();
state->execute("use holy");
// 查询
result = state->executeQuery("select * from student where id next()) {
int id = result->getInt("id");
string name = result->getString("name");
cout << id << " : " << name <ReleaseConnection(con);
}
}
int main(int argc, char* argv[]) {
ConnectMySQL::run();
return 0;
}
暂无评论内容