CGLmysql --数据库连接池
代码
sql_connection_pool.h文件
#ifndef _CONNECTION_POOL_
#define _CONNECTION_POOL_
#include <mysql/mysql.h>
#include"./lock/locker.h"
#include"./log/log.h"
#include<string>
#include<list>
class connection_pool{
private:
int m_MaxConn;
int m_CurConn;
int m_FreeConn;
locker lock;
sem reserve;
list<MYSQL*> connList;
public:
string m_url;
string m_Port;
string m_User;
string m_Password;
string m_DataBaseName;
int m_close_log;
private:
connection_pool();
~connection_pool();
public:
static connection_pool* GetInstance();
void init(string url, string User, string Password, string DataBaseName, int Port, int MaxConn, int close_log);
MYSQL* GetConnection();
bool ReleaseConnection(MYSQL* conn);
int GetFreeConn();
void DestroyPool();
};
class connectionRAII{
private:
MYSQL* connRAII;
connection_pool *poolRAII;
public:
connectionRAII(MYSQL **con, connection_pool* connPool);
~connectionRAII();
};
#endif
sql_connection_poll.cpp文件
#include"sql_connection_pool.h"
#include<stdarg.h>
connection_pool::connection_pool(){
m_CurConn = 0;
m_FreeConn = 0;
}
connection_pool::~connection_pool(){
DestroyPool();
}
connection_pool* connection_pool::GetInstance(){
static connection_pool connPool;
return &connPool;
}
void connection_pool::init(string url, string User, string Password, string DataBaseName,
int Port, int MaxConn, int close_log){
m_url = url;
m_User = User;
m_Password = Password;
m_DataBaseName = DataBaseName;
m_Port = Port;
m_MaxConn = MaxConn;
m_close_log = close_log;
for(int i = 0; i < m_MaxConn; i++){
MYSQL* con = NULL;
con = mysql_init(con);
if(con == NULL){
LOG_ERROR("MySQL mysql_init() error");
exit(1);
}
con = mysql_real_connect(con,url.c_str(),User.c_str(),Password.c_str(),DataBaseName.c_str(),Port,NULL,0);
if(con == NULL){
LOG_ERROR("MySQL mysql_real_connect() error");
exit(1);
}
connList.push_back(con);
m_FreeConn++;
}
reserve = sem(m_FreeConn);
m_MaxConn = m_FreeConn;
}
MYSQL* connection_pool::GetConnection(){
if(connList.size() == 0){
return NULL;
}
reserve.wait();
lock.lock();
MYSQL* con = connList.front();
m_FreeConn--;
m_CurConn++;
lock.unlock();
return con;
}
bool connection_pool::ReleaseConnection(MYSQL* con){
if(con == NULL){
return false;
}
lock.lock();
connList.push_back(con);
m_FreeConn++;
m_CurConn--;
reserve.post();
lock.unlock();
return true;
}
int connection_pool::GetFreeConn(){
int count;
lock.lock();
count = this->m_FreeConn;
lock.unlock();
return count;
}
void connection_pool::DestroyPool(){
lock.lock();
if(connList.size() > 0){
list<MYSQL*>::iterator ite;
for(ite = connList.begin(); ite != connList.end(); ite++){
MYSQL *con = *ite;
mysql_close(con);
}
m_CurConn = 0;
m_FreeConn = 0;
connList.clear();
}
lock.unlock();
}
connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){
*SQL = connPool->GetConnection();
connRAII = *SQL;
poolRAII = connPool;
}
connectionRAII::~connectionRAII(){
poolRAII->ReleaseConnection(connRAII);
}
============================ 总结 ================================
数据库连接池的实现思路
- 单例模式,也就是线程不可以创建池子对象,只能调用这个对象
- 因为静态变量只有一份,而且只会初始化一次,所有,当某个线程(假如当前线程就是创建连接池的线程)想要获取连接池实例时:
- 调用 GetInstance函数,获取连接池实例
- 如果是调用该函数的第一个线程,连接池的构造函数connection_pool()会被调用;此时,连接池的空闲连接数和使用连接数都是0
- 线程获取该实例之后,调用 init函数,创建一些连接,并“存储”到 List< MYSQL* >中(具体地说,这个List其实就是存放很多连接的容器,可以说,连接池,就是,一个含有这个list容器的对象/实例);
- 到这里,连接池存在并且被初始化了,也就是Pool有了。
- 考虑这种情况,某个线程获取一个数据库连接有一种直接的方式,那就是 获取Pool实例, 直接Pool->connList.front(),就可以了;但是,为什么要引入RAII技术呢? RAII技术,他就是充分地使用了类对象自动调用构造函数和析构函数的特性,把释放资源那步放在析构函数中,会自动释放资源,所有,引入创建RAII类,通过这个RAII类对象获取一个数据库连接,然后再销毁这个对象,连接也会被释放。
- 创建一个RAII对象 raII,调用它的有参构造函数,这个函数会让 raii拥有一个连接和连接池(这个连接池对象需要调用GetInstance函数获得)
- 获取,使用,raII的连接, MYSQL* con = raII.connRAII; 通过 raII.poolRAII获取连接池的状态变量
- 线程结束,raII销毁,连接自动释放。
- 为了保证线程安全,这里用到了互斥锁,和 信号量
- 互斥锁的作用是,防止多个线程同时进入临界区(保护的是,m_FreeConn 和 m_CurConn 的修改唯一)
- 信号量的作用是,实现资源的多线程共享,这里的资源是,connList<>池子,多个线程可以同时进入池子里面获取连接。
问题
找不到 #include<mysql/mysql.h>
- 原因:usr的include目录中没有mysqld/mysql.h文件
- 解决:sudo apt-get install libmysqlclient-dev
未定义标识符 "VA_ARGS"C/C++(20)
单例模式中,如果两个线程同时调用 GetInstance函数,会发生什么?是一个线程阻塞,还是都会获得这个实例?
- 单例模式中的单个唯一实例可以被多个线程同时获取。
- 这个实例本身不是临界资源,但是在创建这个实例的过程中,需要确保线程安全,以防止多个线程同时创建多个实例。
- 单例模式中,虽然内存中只有一份静态实例,但是多个线程可以同时访问这个实例。
- 这是因为线程共享进程的地址空间,所以它们可以同时访问同一个内存地址。当多个线程同时调用获取实例的方法时,它们都会返回指向同一个内存地址的指针。
单例模式中,内存中的唯一静态实例,什么时候被销毁?如果这个实例中含有一个List<MYSQL*>容器,在析构函数中应该做些什么?这个List容器需要我手动释放掉吗?如果调用List<MYSQL*>::clear()会发生什么?
- 单例模式中,内存中的唯一静态实例通常在程序结束时被销毁。
- 如果这个实例中含有一个List< MYSQL*>容器,那么在析构函数中应该遍历这个容器,释放每个MYSQL指针指向的内存。
- 如果你调用List< MYSQL>::clear(),它只会清空容器中的元素,但不会释放每个MYSQL*指针指向的内存。
- 所以,你需要手动释放这些内存。