引言
數據庫連接是一種關鍵的有限的昂貴的資源,因此對數據庫連接的管理能顯著影響到整個應用程序的伸縮性和健壯性,影響到程序的性能指標。數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而再不是重新建立一個;釋放空閒時間超過最大空閒時間的數據庫連接來避免因爲沒有釋放數據庫連接而引起的數據庫連接遺漏。這項技術能明顯提高對數據庫操作的性能。
數據庫連接池的關鍵因素
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;
}