日期:2014-05-16  浏览次数:20476 次

如何创建数据库连接池(一个基于libmysql的MySQL数据库连接池示例)

1、  一般架构说明

1 架构层次图

        一般应用系统数据库访问模块可大致分为两层,一层是对数据库连接、连接池和结果集等直接对数据库的操作的封装,由于libmysql提供的库函数是直接操作数据库的,所以这一层在本质上是直接操作数据库的一层;二是可以根据系统业务功能将系统与数据库的交互划分为几个子块,提供给系统其它模块与数据库交互的接口。如果是C/S结构的系统,客户端与数据库的交互还可以通过诸如RPC(Remote Procedure Call Protocol 远程过程调用协议)等协议调用服务端RPC处理模块来进行。

        如上的设计一是隔离了外界与数据库的直接交互,提高了数据安全性;二是对libmysql提供的数据库操作接口众多,统一类操作有多种不同的方式,各自适用的场合不同,对其进行了整合,是为使其更适合本系统,提高了系统的稳定性和可用性,同时使上层对数据库的操作更为方便;三是按系统功能划分子模块降低了系统的耦合度,提高了系统的可扩展性。

        另外,数据库直接交互层也分为三个小块,这样做的目的也是为了降低系统耦合度。其中数据库连接管理块仅负责数据库连接的维持及相应查询事物处理。数据库连接池管理块则是负责在系统初始化时创建一定数量的数据库连接,实际上就是建立两个连接队列(一个在用连接队列和一个空闲连接队列)并维护这两个队列。对于连接池的建立是为了避免在每一次操作数据库时都要建立数据库连接和释放数据库连接等耗时操作,提高系统性能。数据结果集的处理是专门负责将查询返回的结果按上层要求的方式提供给上层使用。

2、  数据库连接类的实现

        本类主要是实现数据库连接的建立和释放,数据库选择,SQL语句的执行,事务的提交和回滚及数据库错误信息的获取等功能。其中数据库连接的建立与释放及查询语句的执行是本节叙述的重点。在libmysql提供的库中使用mysql_real_connect()可与MySQL数据库建立连接,但在这之前还需要对MYSQL实例进行初始化,其中MYSQL为MySQL的数据库连接句柄,具体实现过程如下。

        a、使用mysql_init()创建MYSQL句柄实例;

        b、根据数据库主机地址、用户名、用户密码、数据库名及端口号等连接信息使用mysql_real_connect()接口为MYSQL句柄实例建立数据库连接;

        c、为已经建立数据库连接的MYSQL句柄实例选择字符集。

        需要注意的是,当应用程序服务器和数据库服务器不在同一主机时,新安装的MySQL数据库处于安全考虑是不允许远程连接的,这就需要为相应的用户赋予远程访问的权限,可采用MySQL命令:
GRANT EXECUTE ON DBName.TableName TO 'UserName'@'192.168.1.1' IDENTIFIED BY 'UserPassword';

        其中“192.168.1.1”为远程主机的IP地址,使用“%”表示接受所有IP主机的远程访问。当然出于数据安全考虑,需要慎重执行。最终实现连接建立的代码如下:
int CDBConnect::ConnectDB(const char *host, const char *user, const char *password, const char *db, unsigned int port)
{
	if (m_pMySql != NULL)
	{
		if( NULL != pLC )
		{
			pLC->Trace( __FILE__, __LINE__, "m_pMySql 已经初始化\n");
		}
		return DATABASE_NOERROR;
	}

	m_pMySql = mysql_init((MYSQL*)NULL);

	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Fatal( __FILE__, __LINE__, "m_pMySql 初始化失败\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	
	if (!mysql_real_connect(m_pMySql, host, user, password, db, port,NULL,0))
	{
		if( NULL != pLC )
		{
			pLC->Fatal( __FILE__, __LINE__, "数据库连接失败,%s\n", GetLastErrorMsg());
		}
		int iRet = mysql_errno(m_pMySql);
		printf( "errno = %d\n", iRet );
		if (iRet == 1045)
		{  
			return DATABASE_ERROR_USERORPASSWORD;
		}
		return DATABASE_ERROR_GET_CONNECTION;
	}

	if (mysql_set_character_set(m_pMySql, "gbk") != 0) 
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "数据库字符集设置失败,%s\n", GetLastErrorMsg());
		}
		return DATABASE_POOL_ERROR_SET_CHARACTER;
	}

	return DATABASE_NOERROR;
}

void CDBConnect::Release()
{
	delete this;
}

void CDBConnect::CloseConnect()
{