【服务器编程】MYSQL数据库连接池封装

【前言】

现代服务器后台数据基本上都用数据库管理,因为他有一套完整的数据保存方案,本连接池是根据MYSQL提供的纯C API封装。

【上回战况】

目前,服务器添加了文件服务器类,只是简单的文件传输;但是没有使用数据库保存文件的信息,随着数据的增加,文件的管理肯定就很难。

【目前战况】

为了同意所有线程访问数据库的方式,封装一个连接池,如下:

class MYSQLConnPool
{
public:
	typedef struct 
	{
		bool flag;   /* 判断此连接是否已经被占用(true) */
		MYSQL *conn;
	}SMYSQL;
public:
	/* Singleton模式 */
	static MYSQLConnPool* CreateConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num)
	{
		if (conn_num > MAX_CONN_NUM)
			return nullptr;
		if (m_ConnPool == nullptr)
		{
			m_ConnPool = new MYSQLConnPool(host, user, psw, db, conn_num);
			if (m_ConnPool)
				return m_ConnPool;
			return nullptr;
		}
		return nullptr;
	}
	/* 成功返回该调用线程所使用连接的索引,出错返回mysql_query对应的返回值 */
	int Query(const char *sql);
	/* 根据Query返回值查询指定索引连接的返回结果 */
	MYSQL_RES *StoreResult(int idx);
	/* 释放结果后才能释放连接(使连接处于空闲可被调用) */
	void FreeResult(MYSQL_RES *res, int idx);

	~MYSQLConnPool()
	{
		for (int i = 0; i < m_ConnNum; ++i)
		{
			mysql_close(m_ConnVec[i].conn);
		}
	}

private:
	MYSQLConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num);

private:
	/* 连接池最大连接数 */
	static const int MAX_CONN_NUM = 16;
	/* 连接池的连接数 */
	int m_ConnNum;
	pthread_mutex_t m_ListMutex;
	sem_t m_ListSem;
	/* 连接列表 */
	std::vector<SMYSQL> m_ConnVec;

	static MYSQLConnPool *m_ConnPool;
};

MYSQLConnPool* MYSQLConnPool::m_ConnPool = nullptr;

MYSQLConnPool::MYSQLConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num)
{
	/* 根据连接数量初始化信号量 */
	int ret = sem_init(&m_ListSem, 0, conn_num);
	m_ListMutex = PTHREAD_MUTEX_INITIALIZER;
	m_ConnNum = conn_num;
// 建立数据库连接
	for (int i = 0; i < conn_num; ++i)
	{
		MYSQL *conn = mysql_init(nullptr);
		if (!conn)
			continue;
		conn = mysql_real_connect(conn, host, user, psw, db, 0, nullptr, 0);
		if (!conn)
			continue;
		m_ConnVec.push_back({false, conn});
	}
}

int MYSQLConnPool::Query(const char *sql)
{
	sem_wait(&m_ListSem);
	/* 有空闲连接,设置flag,相当于上锁 */
	int idx = 0;
	pthread_mutex_lock(&m_ListMutex);
	for (idx = 0; idx < m_ConnNum; ++idx)
	{
		if (!m_ConnVec[idx].flag)
		{
			m_ConnVec[idx].flag = true;
			break;
		}
	}
	pthread_mutex_unlock(&m_ListMutex);
	/* 执行查询 */
	int ret = mysql_query(m_ConnVec[idx].conn, sql);
	return ret == 0?idx:ret;  // 如果uqery成功,直接返回该连接的索引,供store/freestore调用。
}

MYSQL_RES *MYSQLConnPool::StoreResult(int idx)
{
	return mysql_store_result(m_ConnVec[idx].conn);
}

void MYSQLConnPool::FreeResult(MYSQL_RES *res, int idx)
{
	mysql_free_result(res);
	m_ConnVec[idx].flag = false;
	sem_post(&m_ListSem);
}
这里使用了singleton模式,每个子进程只允许创建一个连接池。使用vector保存所有连接,同时通过互斥量和信号量防止竞争。

我的想法:既然我们的目的是封装一个连接池,那么我们关心的更多是连接池的管理,关于MYSQL数据库的访问应该尽可能脱离连接池的封装。所以,这里我只将查询/返回结果/释放结果进行封装,这样做我还有另一个原因:因为如果直接将连接变量(MYSQL*)返回,然后在客户程序是直接使用MYSQL提供的纯C API,那么就破坏了封装性。另外,需要注意的是:对于每个连接变量被用于query调用时,在下次再query前,必须调用store/freestore来释放结果。所以,我更坚定的封装这三个函数。


【Github】

点击打开链接



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值