Mysql数据库学习——C++实现Mysql数据库连接池

0 数据库连接池材料清单

Mysql数据库连接池包含以下关键组成部分:

  1. 数据库连接队列

    数据库最大连接数
    当前已用连接数
    当前可用连接数
    
  2. 并发安全控制参数

    数据库连接队列的**互斥锁**
    数据库连接队列的**同步信号量**
    

1 两处优化

  1. 单例函数get_instance–用于保证连接池对象全局唯一
  2. connectionRAII类–维护和管理连接池对象的生命周期

2 实现代码

主要包含sql_connection_pool.h、sql_connection_pool.cpp以及locker.h,完整代码参见https://github.com/qinguoyi/TinyWebServer/tree/master/CGImysql。

2.1 sql_connection_pool.h

该文件包含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

2.2 sql_connection_pool.cpp

#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);
}

2.3 locker.h

#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

3 测试

测试代码。

#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]);
    }
}

4 参考材料

1 https://zhuanlan.zhihu.com/p/95811114
2 https://github.com/qinguoyi/TinyWebServer/tree/master/CGImysql

你可能感兴趣的:(Mysql,数据库连接池)