Mysql数据库连接池包含以下关键组成部分:
数据库连接队列
数据库最大连接数
当前已用连接数
当前可用连接数
并发安全控制参数
数据库连接队列的**互斥锁**
数据库连接队列的**同步信号量**
主要包含sql_connection_pool.h、sql_connection_pool.cpp以及locker.h,完整代码参见https://github.com/qinguoyi/TinyWebServer/tree/master/CGImysql。
该文件包含connection_pool和connectionRAII两个类。
#ifndef _CONNECTION_POOL_
#define _CONNECTION_POOL_
#include
#include
#include
#include
#include
#include
#include
#include "../lock/locker.h"
#include "../log/log.h"
using namespace std;
class connection_pool
{
public:
MYSQL *GetConnection(); //获取数据库连接
bool ReleaseConnection(MYSQL *conn); //释放连接
int GetFreeConn(); //获取可用连接数
void DestroyPool(); //销毁所有连接
//单例模式
static connection_pool *GetInstance();// 返回一个connection_pool数据库连接池对象 全局唯一
// 初始化连接池
void init(string url, string User, string PassWord, string DataBaseName, int Port, int MaxConn, int close_log);
private:
connection_pool();
~connection_pool();
int m_MaxConn; //最大连接数
int m_CurConn; //当前已使用的连接数
int m_FreeConn; //当前空闲的连接数
locker lock;// 互斥信号量
list<MYSQL *> connList; //连接池
sem reserve;// 条件/同步信号量 其初始值为m_FreeConn
public:
string m_url; //主机地址
string m_Port; //数据库端口号
string m_User; //登陆数据库用户名
string m_PassWord; //登陆数据库密码
string m_DatabaseName; //使用数据库名
int m_close_log; //日志开关
};
// connectionRAII用于管理connection_pool连接池对象的生命周期
class connectionRAII{
public:
connectionRAII(MYSQL **con, connection_pool *connPool);
~connectionRAII();
private:
MYSQL *conRAII;
connection_pool *poolRAII;
};
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "sql_connection_pool.h"
using namespace std;
connection_pool::connection_pool()
{
m_CurConn = 0;
m_FreeConn = 0;
}
connection_pool *connection_pool::GetInstance()
{
// 没有线程安全
static connection_pool connPool;
return &connPool;
}
//构造初始化
void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log)
{
m_url = url;
m_Port = Port;
m_User = User;
m_PassWord = PassWord;
m_DatabaseName = DBName;
m_close_log = close_log;
// 初始化数据库连接队列
for (int i = 0; i < MaxConn; i++)
{
MYSQL *con = NULL;
con = mysql_init(con);
if (con == NULL)
{
printf("MySQL Error");
exit(1);
}
con = mysql_real_connect(con, m_url.c_str(), m_User.c_str(), m_PassWord.c_str(), m_DatabaseName.c_str(), Port, NULL, 0);
if (con == NULL)
{
printf("MySQL Error");
exit(1);
}
// 加入connList队列
connList.push_back(con);
++m_FreeConn;
}
// 初始化同步/条件信号量
reserve = sem(m_FreeConn);
// 更新/初始化m_MaxConn
m_MaxConn = m_FreeConn;
}
// 当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数
MYSQL *connection_pool::GetConnection()
{
MYSQL *con = NULL;
if (0 == connList.size())
return NULL;
// 条件/同步信号量
reserve.wait();// P操作 当可用连接数为0时,进入阻塞
// 上锁
lock.lock();
// 从connList连接队列中取出一个连接
con = connList.front();
connList.pop_front();
// 更新连接池相关参数
--m_FreeConn;
++m_CurConn;
lock.unlock();
return con;// 一个MYSQL*实例
}
//释放当前使用的连接
bool connection_pool::ReleaseConnection(MYSQL *con)
{
if (NULL == con)
return false;
lock.lock();
connList.push_back(con);
++m_FreeConn;
--m_CurConn;
lock.unlock();
reserve.post();// 释放一个同步信号量 V操作
return true;
}
//销毁数据库连接池
void connection_pool::DestroyPool()
{
lock.lock();
if (connList.size() > 0)
{
list<MYSQL *>::iterator it;
for (it = connList.begin(); it != connList.end(); ++it)
{
MYSQL *con = *it;
mysql_close(con);
}
m_CurConn = 0;
m_FreeConn = 0;
connList.clear();
}
lock.unlock();
}
//当前空闲的连接数
int connection_pool::GetFreeConn()
{
return this->m_FreeConn;
}
connection_pool::~connection_pool()
{
DestroyPool();
}
connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){
// 从connection_pool连接池中获取一个Mysql连接
*SQL = connPool->GetConnection();// 该SQL为传出参数
conRAII = *SQL;
// 获取一个connection_pool数据库连接池对象(指针)
poolRAII = connPool;
}
connectionRAII::~connectionRAII(){
poolRAII->ReleaseConnection(conRAII);
}
#ifndef LOCKER_H
#define LOCKER_H
#include
#include
#include
class sem
{
public:
sem()
{
if (sem_init(&m_sem, 0, 0) != 0)
{
throw std::exception();
}
}
sem(int num)
{
if (sem_init(&m_sem, 0, num) != 0)
{
throw std::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
bool wait()
{
return sem_wait(&m_sem) == 0;
}
bool post()
{
return sem_post(&m_sem) == 0;
}
private:
sem_t m_sem;
};
class locker
{
public:
locker()
{
if (pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
}
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
bool unlock()
{
return pthread_mutex_unlock(&m_mutex) == 0;
}
pthread_mutex_t *get()
{
return &m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
class cond
{
public:
cond()
{
if (pthread_cond_init(&m_cond, NULL) != 0)
{
//pthread_mutex_destroy(&m_mutex);
throw std::exception();
}
}
~cond()
{
pthread_cond_destroy(&m_cond);
}
bool wait(pthread_mutex_t *m_mutex)
{
int ret = 0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_wait(&m_cond, m_mutex);
//pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
bool timewait(pthread_mutex_t *m_mutex, struct timespec t)
{
int ret = 0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);
//pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
bool signal()
{
return pthread_cond_signal(&m_cond) == 0;
}
bool broadcast()
{
return pthread_cond_broadcast(&m_cond) == 0;
}
private:
//static pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif
测试代码。
#include
#include
#include "connection_pool/sql_connection_pool.h"
int main() {
connection_pool* conn_pool = connection_pool::GetInstance();
conn_pool->init("127.0.0.1", "root", "", "tinywebserver", 3306, 8, 0);
std::cout << "conn_poot init done\n";
MYSQL* mysql = nullptr;
connectionRAII mysqlconn(&mysql, conn_pool);// 使用connectionRAII管理该connPool线程池实例的生命周期
//在user表中检索username,passwd数据,浏览器端输入
if (mysql_query(mysql, "SELECT username,passwd FROM user"))
{
printf("SELECT error:%s\n", mysql_error(mysql));
}
//从表中检索完整的结果集
MYSQL_RES *result = mysql_store_result(mysql);
//返回结果集中的列数
int num_fields = mysql_num_fields(result);
//返回所有字段结构的数组
MYSQL_FIELD *fields = mysql_fetch_fields(result);
//从结果集中获取下一行,将对应的用户名和密码,存入map中
while (MYSQL_ROW row = mysql_fetch_row(result))
{
printf("%s:%s\n", row[0], row[1]);
}
}
1 https://zhuanlan.zhihu.com/p/95811114
2 https://github.com/qinguoyi/TinyWebServer/tree/master/CGImysql