TinyWebServer学习笔记-MySQL数据库连接

为什么要用数据库连接池?

我们的网站允许用户注册,在没有池的情况下,假设只有一个人,那么流程就是,用户点击注册,通过socket将用户的账号和密码发送到服务器,然后就需要创建MySQL数据库连接,然后插入数据,插入完毕后销毁该连接;然而当用户很多时,这个设计就会花费大量的时间在数据库连接的创建和释放上。为了避免这种情况,引入池的概念,在程序初始化的时候,集中建立多个数据库连接,并集中管理,供程序使用,更见安全和可靠。

池是一组资源的机会,这组资源在服务器启动之初就被完全创建好并初始化,池是资源的容器,实现对资源的复用。是一种用空间换时间的技术。当系统开始处理客户请求的时候,如果需要相关资源,直接从池中获取,无需动态分配,当服务器处理完一个客户连接口,将相关资源放回池中,无需执行系统调用来释放资源。

如何设计?

class connection_pool
{
public:
	MYSQL *GetConnection();				 //获取数据库连接
	bool ReleaseConnection(MYSQL *conn); //释放连接
	int GetFreeConn();					 //获取连接
	void DestroyPool();					 //销毁所有连接

	//单例模式
	static connection_pool *GetInstance();

	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 connList; //连接池
	sem reserve;

public:
	string m_url;			 //主机地址
	string m_Port;		     //数据库端口号
	string m_User;		     //登陆数据库用户名
	string m_PassWord;	     //登陆数据库密码
	string m_DatabaseName;   //使用数据库名
	int m_close_log;	     //日志开关
};

在init函数中完成了对各个数据库连接的初始化,并将连接存储到list中,

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)
		{
			LOG_ERROR("MySQL Error");
			exit(1);
		}
		con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);

		if (con == NULL)
		{
			LOG_ERROR("MySQL Error");
			exit(1);
		}
		connList.push_back(con);
		++m_FreeConn;
	}

	reserve = sem(m_FreeConn);

	m_MaxConn = m_FreeConn;
}

下面是从池中获取一个连接的函数:

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();
	return true;
}

可以看到,先调用wait函数,reserve是信号量,如果值>=1,那么将值-1,如果为0就会陷入阻塞。成功获得信号量后,获得互斥锁,然后将池中的list的头取出来,更新相关的数据后释放锁。

释放连接时也是类似的,上锁,然后将连接添加进池中,更新数据后释放锁。

而作者为了实现池的RAII,设计了connectionRAII类:

class connectionRAII{

public:
	connectionRAII(MYSQL **con, connection_pool *connPool);
	~connectionRAII();
	
private:
	MYSQL *conRAII;
	connection_pool *poolRAII;
};

需要说明,由于数据库连接本身就是指针,因此通过有参构造对传入的参数进行修改时需要用双指针。

TinyWebServer学习笔记-MySQL数据库连接_第1张图片

如图所示,*A的值是0x00000004;*B的值是C的实际内容,那么也就是说*A之后得到0x00000008是实际资源的地址,现在还要"解引用"一次才能得到实际值。

下面的代码是获得池中的一个连接以及将连接还回去的代码。

connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){
	*SQL = connPool->GetConnection();
	
	conRAII = *SQL;
	poolRAII = connPool;
}

connectionRAII::~connectionRAII(){
	poolRAII->ReleaseConnection(conRAII);
}

你可能感兴趣的:(TinyWebServer笔记,数据库,学习,笔记)