数据库连接池类
使用池的原因是因为数据库的连接和释放很消耗资源,如果每次需要数据库的时候再进行连接非常耗时。 使用单例模式
class connection_pool
接口:
static connect_pool *GetInstance() :单例模式,获取数据库池对象指针 void init(…):数据库初始化,输入数据库的用户密码和对哪个数据库进行操作。 MYSQL *GetConnection() :获取数据库连接 ,无可用连接时候会阻塞。 bool ReleaseConnection(MYSQL*) :把之前获取到的数据库连接放回去。 int GetFreeConn():当前连接池有多少可用连接。
实现
# include "mysql/mysql.h"
# include "../lock/locker.h"
# include "list"
# include "string"
class connection_pool {
public :
static connection_pool * GetInstance ( ) ;
void init ( std:: string host, int port, std:: string user_name, std:: string password, std:: string db_name, int max_conn) ;
MYSQL * GetConnection ( ) ;
bool ReleaseConnection ( MYSQL* & ) ;
int GetFreeConn ( ) {
return m_free_conn;
}
private :
connection_pool ( ) ;
~ connection_pool ( ) ;
private :
std:: list< MYSQL* > m_list;
int m_free_conn;
sem m_sem;
locker m_locker;
} ;
connection_pool:: connection_pool ( ) : m_free_conn ( 0 ) { }
connection_pool :: ~ connection_pool ( ) {
MutexLockGuard lock ( m_locker) ;
for ( auto iter = m_list. begin ( ) ; iter != m_list. end ( ) ; ++ iter) {
if ( * iter != nullptr )
mysql_close ( * iter) ;
}
}
connection_pool * connection_pool:: GetInstance ( ) {
static connection_pool m_instance;
return & m_instance;
}
void connection_pool:: init ( std:: string host, int port, std:: string user_name, std:: string password, std:: string db_name, int max_conn) {
MutexLockGuard lock ( m_locker) ;
if ( max_conn <= 0 )
max_conn = 3 ;
for ( int i = 0 ; i != max_conn; ++ i) {
MYSQL * mysql_handler = nullptr ;
mysql_handler = mysql_init ( mysql_handler) ;
if ( ! mysql_handler) {
printf ( "mysql connect error\n" ) ;
continue ;
}
mysql_handler = mysql_real_connect ( mysql_handler, host. c_str ( ) , user_name. c_str ( ) , password. c_str ( ) , db_name. c_str ( ) , port, nullptr , 0 ) ;
if ( ! mysql_handler) {
printf ( "mysql connect error\n" ) ;
continue ;
}
m_list. push_back ( mysql_handler) ;
++ m_free_conn;
}
m_sem = sem ( m_free_conn) ;
}
MYSQL * connection_pool:: GetConnection ( ) {
MYSQL * res = nullptr ;
if ( m_list. empty ( ) )
return res;
m_sem. wait ( ) ;
{
MutexLockGuard lock ( m_locker) ;
res = m_list. front ( ) ;
m_list. pop_front ( ) ;
-- m_free_conn;
}
return res;
}
bool connection_pool:: ReleaseConnection ( MYSQL * & conn) {
if ( conn == nullptr ) return false ;
MutexLockGuard lock ( m_locker) ;
m_list. push_back ( conn) ;
++ m_free_conn;
m_sem. post ( ) ;
conn = nullptr ;
return true ;
}
要点:
单例模式采用的是C++0x之后才支持的线程安全的static初始化的一个操作,也就是
connection_pool * connection_pool:: GetInstance ( ) {
static connection_pool m_instance;
return & m_instance;
}
在析构函数的时候关闭mysql连接,由于对数据库连接池采用的是单例模式,所以之后进程结束才会进行析构,也就不用考虑有些mysql连接还没释放的情况了。
for ( auto iter = m_list. begin ( ) ; iter != m_list. end ( ) ; ++ iter) {
if ( * iter != nullptr )
mysql_close ( * iter) ;
}
释放连接的时候对MYSQL*参数使用的是引用传参,并且在放回m_list之后回把参数置nullptr,这样就防止了一边把数据库资源还给连接池,一边继续使用的情况发生。
bool connection_pool:: ReleaseConnection ( MYSQL * & conn) {
if ( conn == nullptr ) return false ;
MutexLockGuard lock ( m_locker) ;
m_list. push_back ( conn) ;
++ m_free_conn;
m_sem. post ( ) ;
conn = nullptr ;
return true ;
}
总结:
使用之前要先init()初始化,并且注意init函数不能多次调用,多次调用会引发不可预测的后果。 使用GetConnection()获取一个MYSQL*数据库连接,获取连接的时候不会阻塞,当发现连接池中没有可用连接后,直接返回nullptr。 ReleaseConnection()释放一个MYSQL*连接。
添加:使用RAII管理数据库连接池,用来自动释放连接。
使用数据库连接池初始化ConnectRAII,初始化之后的ConnectRAII对象会持有一个数据库连接对象,通过getConn获取这个对象,当离开作用域后,这个对象会自己释放。
class ConnectRAII {
public :
ConnectRAII ( connection_pool* connectpool) {
m_mysql = connectpool-> GetConnection ( ) ;
m_conn_pool = connectpool;
}
~ ConnectRAII ( ) {
m_conn_pool-> ReleaseConnection ( m_mysql) ;
}
MYSQL* getConn ( ) {
return m_mysql;
}
private :
MYSQL* m_mysql;
connection_pool* m_conn_pool;
} ;