引言
数据库连接是一种关键的有限的昂贵的资源,因此对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
数据库连接池的关键因素
1.容器
数据库连接池是数据库连接的集合,必须要有容器来存储这些连接,许多方法都是用stl的list、queue、vector常用的容器来实现的,但今天我选择了circular_buffer_space_optimized来实现,具体介绍可到www.boost.org上查看。
2.最小连接数
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。
3.最大连接数
连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
4.连接的使用
最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。获得连接的请求使用完之后,要把连接放回容器当中,以便之后的请求重复使用,达到重复利用的目的。
实现代码
当然,我也不完全按照上面的主要因素来实现的,可根据circular_buffer_space_optimized和OTL的特性实现了一个简单的数据库连接池。
//数据库连接池虚类db_conn_pool.h
#pragma once
#include <iostream>
#include <boost/circular_buffer.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
template <class conn>
class db_conn_pool
{
public:
typedef conn connection;
typedef boost::shared_ptr<conn> connection_ptr;
virtual connection_ptr getConnection() = 0;
virtual void initConnection() = 0;
int size(){return m_conn_container.size();}
protected:
std::string m_conn_str;
unsigned short m_max_size;
boost::circular_buffer_space_optimized<connection_ptr> m_conn_container;
boost::mutex m_mutex;
db_conn_pool(){}
db_conn_pool(const std::string& conn_str, unsigned short max_size)
:m_conn_str(conn_str), m_max_size(max_size)
{
}
virtual connection_ptr createConnection() = 0;
};
template<class conn_pool, class conn>
class connection_wrap{
public:
connection_wrap(conn_pool& pool, boost::shared_ptr<conn> ptrConn)
:m_pool(pool), m_ptrConn(ptrConn)
{
}
~connection_wrap()
{
m_pool.releaseConnection(m_ptrConn);
}
private:
boost::shared_ptr<conn> m_ptrConn;
conn_pool& m_pool;
};
//mysql数据库连接池my_conn_pool.h
#pragma once
#include <iostream>
#define OTL_ODBC // CompileOTL 4.0/ODBC
// Thefollowing #define is required with MyODBC 5.1 and higher
#define OTL_ODBC_SELECT_STM_EXECUTE_BEFORE_DESCRIBE
#define OTL_STL
#include "otlv4.h"
#include "db_conn_pool.h"
#include <boost/format.hpp>
#include <firebird/log/logger_log4.hpp>
//FUNC DEFINE
#define OTL_STREAM _otl_stream
#define OTL_EOF _otl_stream.eof()
#define OTL_SUCCEED 0
#define OTL_FAILED -1
#define OTL_NO_DATA_FOUND 1403
#define OTL_BEGIN(conn, sql) \
try { \
LOG4CXX_DEBUG(console_log, KDS_CODE_INFO << sql); \
otl_stream _otl_stream(100, sql, *conn);
#define OTL_END(conn, _count) \
_otl_stream.flush(); \
_count = _otl_stream.get_rpc(); \
} catch (otl_exception& otl_exp) { \
print_exception(otl_exp); \
if(otl_exp.code == 2006) \
{ \
conn->logoff(); \
} \
throw business_error(otl_exp.code, (char*)(otl_exp.msg)); \
}
#define OTL_COMMIT(conn) conn->commit();
#define OTL_ROLLBACK(conn) conn->rollback();
static void print_exception(otl_exception& otl_exp)
{
boost::format fmt("otl with exception:[%1%][%2%][%3%]");
std::string str = boost::str(fmt
% otl_exp.code
% otl_exp.msg
% otl_exp.stm_text);
LOG4CXX_ERROR(console_log, KDS_CODE_INFO << str);
}
class mysql_conn_pool : public db_conn_pool<otl_connect>
{
public:
~mysql_conn_pool();
static mysql_conn_pool& getInstance();
void setParam(const std::string& conn_str, unsigned short max_size);
connection_ptr getConnection();
void releaseConnection(connection_ptr ptrConn);
void initConnection();
protected:
typedef db_conn_pool<otl_connect> super;
mysql_conn_pool();
private:
connection_ptr createConnection();
};
//my_conn_pool.cpp
#include "mysql_conn_pool.h"
#include <boost/typeof/typeof.hpp>
mysql_conn_pool::mysql_conn_pool()
{
}
mysql_conn_pool::~mysql_conn_pool()
{
}
mysql_conn_pool& mysql_conn_pool::getInstance()
{
static mysql_conn_pool pool;
return pool;
}
void mysql_conn_pool::setParam(const std::string& conn_str, unsigned short max_size)
{
m_conn_str = conn_str;
m_max_size = max_size;
}
void mysql_conn_pool::initConnection()
{
m_conn_container.resize(m_max_size);
otl_connect::otl_initialize(1); // initialize the database API environment
for (int i = 0; i < m_max_size; ++i)
{
createConnection();
}
}
mysql_conn_pool::connection_ptr mysql_conn_pool::getConnection()
{
connection_ptr ptrConn;
std::time_t begin;
std::time(&begin);
while(1)
{
boost::mutex::scoped_lock lock(m_mutex);
if (m_conn_container.size() == 0)
{
std::time_t now;
std::time(&now);
if (now - begin > 10)
{
/*
*若超过10秒还没取得连接对象,则认为连接池里的连接都失效用完,
*应重新创建
*/
createConnection();
begin = now;
}
continue;
}
ptrConn = m_conn_container.front();
m_conn_container.pop_front();
if (ptrConn != NULL && ptrConn->connected)
{
/*BOOST_AUTO(pos, m_conn_container.begin());
m_conn_container.rotate(++pos);*/
break;
}
else
{
//m_conn_container.pop_front();
createConnection();
continue;;
}
}
return ptrConn;
}
mysql_conn_pool::connection_ptr mysql_conn_pool::createConnection()
{
connection_ptr ptrConn(new otl_connect());
ptrConn->rlogon(m_conn_str.c_str());
if (ptrConn != NULL && ptrConn->connected)
{
ptrConn->auto_commit_on();
ptrConn->set_timeout(60);
m_conn_container.push_back(ptrConn);
}
return ptrConn;
}
void mysql_conn_pool::releaseConnection(connection_ptr ptrConn)
{
boost::mutex::scoped_lock lock(m_mutex);
if (ptrConn != NULL && ptrConn->connected)
{
m_conn_container.push_back(ptrConn);
}
}
//测试test.cpp
#include <iostream>
#include "my_conn_pool.h"
int main()
{
mysql_conn_pool& m_mysql_pool = mysql_conn_pool::getInstance();
m_mysql_pool.setParam("Driver={MySQL ODBC 5.2w Driver};Server=127.0.0.1;Port=3306;Database=opensips;Uid=root;Pwd=123321", 10);
m_mysql_pool.initConnection();
mysql_conn_pool::connection_ptr pConn = m_mysql_pool.getConnection();
connection_wrap<mysql_conn_pool, mysql_conn_pool::connection> wrap(m_mysql_pool, pConn);
std::string sql;
sql = "delete from account_bind where ext_acct = :f1<char[50]>";
int count = 0;
OTL_BEGIN(pConn, sql.c_str());
OTL_STREAM << req.sExtAcct;
OTL_END(pConn, count);
return 0;
}