游戏服务器之mysql句柄连接池

mysql连接使用封装原理是建立mysql连接句柄池。mysql连接句柄池在初始化时会根据放入的连接地址读取所有的表(建立表对象及其相关字段)和初始化一个mysql连接句柄。mysql连接句柄池可根据哈希和使用标识获取没有标识使用的句柄。


设计上:

(1)封装mysql连接到mysql连接句柄。mysql连接句柄由句柄管理器管理(加入和读取要加读写锁)。

(2)加载连接的表及其表的字段到表对象。表对象由表结构管理器管理(加入和读取要加读写锁)。实现orm的检查功能。

(3)封装条件查询。实现简化和安全使用字段和条件组成sql。

(4)封装结果集。实现简化获取执行sql结果。


数据结构:

(1)表结构管理器 

std::vector<mysql_table_manager *> tm;  //数据库表管理器 数组

获取表如

mysql_table* table  = this->pool->tm[this->_hashcode]->getTableByName(tablename);//pool 为句柄连接池


(2)句柄管理器

std::vector<mysql_handle_manager *> mhm;//mysql句柄数组,由哈希获取句柄管理者,再遍历获取没有标识使用的句柄。根据实际应用情况分散hashcode 的桶上的连接对象 可以有效减少多线程使用mysql句柄的竞争。


目录:

1、mysql句柄池

(1)句柄池初始化mysql句柄池

(2)加载连接地址

(2-1)加载表格

(3)获取句柄

2、条件查询

(1)条件查询封装

(2)执行sql

(3)查询使用实例


内容:

1、mysql句柄池

每个mysql句柄就是个mysql连接。句柄池会用哈希结构存储mysql句柄,按哈希结构存储mysql连接上的所有表结构。

(1)句柄池初始化mysql句柄池

/**
 * \description 构造函数
 * \param max_hash 支持最大的hash个数
 * \param max_handle 每个hash桶中支持的最多handle个数
 */

mysql_handle_pool::mysql_handle_pool(int max_hash,int max_handle)//int max_hash = 3,int max_handle = 64
{
	for(int i = 0 ; i < max_hash ; i ++)
	{
		mhm.push_back(new mysql_handle_manager);
		tm.push_back(new mysql_table_manager);
	}
	_max_hash = max_hash;
	_max_handle = max_handle;
	g_log->info("Version of the mysql libs is %s" ,mysql_get_client_info());
	if(!mysql_thread_safe())
	{       
		g_log->warn("The mysql libs is not thread safe...");
	}  
}

(2)加载连接地址

生成连接句柄。加载连接地址上的所有表结构。

bool mysql_handle_pool::putUrl(unsigned int hashcode,const char *url)
{
	mysql_url *mu = new mysql_url(hashcode,url);//生成连接句柄
	if(!mu)
	{
		return false;
	}
	if(!mum.addMysqlUrl(mu))
	{
		SAFE_DELETE(mu);
		return false;
	}
	mysql_handle *handle = new mysql_handle(mu,this,hashcode%_max_hash);
	if(!handle)
	{
		return false;
	}
	if(!handle->initHandle())//初始化mysql连接
	{
		SAFE_DELETE(handle);
		return false;
	}
	if(!mhm[hashcode]->addMysqlHandle(handle))
	{
		return false;
	}
	tm[hashcode]->reloadAllTables(handle->getMysql());//加载连接上的所有表格
	return true;
}

使用实例

mysqlPool = new mysql_handle_pool;
if(mysqlPool == NULL ||!mysqlPool->putUrl(0,g_xml_config.get_string("Global.MYSQL.Config")))
{
	g_log->error("连接数据库失败");
	return false;
}

连接配置如: "mysql://root:123456@192.168.188.87:3306/mu"


(2-1)加载表格

bool mysql_table_manager::reloadAllTables(MYSQL *mysql_conn)
{
	MYSQL_RES* table_res = NULL;

	if((table_res = mysql_list_tables(mysql_conn, NULL)) == NULL)//加载所有表格
	{
		g_log->error("%s:mysql_list_tables fail.", __FUNCTION__);
		g_log->error("%s:reason: %s", __FUNCTION__,mysql_error(mysql_conn));
		return false;
	}           
	MYSQL_ROW row;

	delete_all();

	while((row=mysql_fetch_row(table_res)))
	{
		this->addNewTable(mysql_conn, row[0]);
	}

	mysql_free_result(table_res);
	return true;
}

bool mysql_table_manager::addNewTable(MYSQL *mysql_conn,const char *tablename)
{
	mysql_table *table = new mysql_table;
	if(!table)
	{
		return false;
	}
	strncpy(table->name,tablename,sizeof(table->name));
	table->reloadAllFields(mysql_conn , tablename);
	return addTable(table);
}


bool addTable(mysql_table *entry)
{
	int len = strlen(entry->name);
	for (int i = 0; i < len; i++)
	{
		entry->name[i] = ::toupper(entry->name[i]);
	}
	
	bool bret = false;
	rwlock.wrlock();
	bret = add_object(entry);
	rwlock.unlock();
	return bret;
}


加载字段

bool mysql_table::reloadAllFields(MYSQL *mysql_conn,const char *tablename)
{
	MYSQL_RES* field_res = NULL;
	field_res = mysql_list_fields(mysql_conn, tablename, NULL);
	if(!field_res)
	{
		return false;
	}
	unsigned int num_fields = mysql_num_fields(field_res);
	MYSQL_FIELD* mysql_fields = NULL;
	mysql_fields = mysql_fetch_fields(field_res);

	for(unsigned int i=0; i<num_fields; i++)
	{
		if(!fs.addNewField(mysql_fields[i].name, mysql_fields[i].type))
		{
			mysql_free_result(field_res);
			return false;
		}
	}

	mysql_free_result(field_res);
	return true;
}
bool mysql_record::addNewField(const char *fieldname, int fieldType, uint32 mask)
{
	mysql_field *entry = new mysql_field;
	if(!entry)
	{
		return false;
	}
	strncpy(entry->name,fieldname,sizeof(entry->name));
	entry->type = fieldType;
	entry->mask = mask;
	return addField(entry);
}

bool mysql_record::addField(mysql_field *entry)
{
	for(unsigned int i = 0;entry->name[i]&&i < sizeof(entry->name);++i)
		entry->name[i] = (char)::toupper(entry->name[i]);
        g_log->trace("%s,%u, %s", __PRETTY_FUNCTION__, __LINE__, entry->name.c_str());
	bool bret = false;
	rwlock.wrlock();
	bret = add_object(entry);
	rwlock.unlock();
	return bret;
}


(3)获取句柄

mysql_handle *mysql_handle_pool::getHandle(uint32 hashcode)
{
	struct GetHandleExec :public callback<mysql_handle>
	{
		GetHandleExec():_handle(NULL)
		{
		}
		mysql_handle *_handle;
		bool invoke(mysql_handle *entry)
		{
			switch(entry->state)
			{
				case HandleState_Valid:
				case HandleState_Invalid:
					{
						if(entry->setHandle())//设置句柄
						{
							_handle = entry;
							return false;
						}
					}
					break;
				case HandleState_Used:
					{
						entry->checkUseTime();//检查使用句柄时间
					}
					break;
			}
			return true;
		}
	};
	GetHandleExec exec;
        _mutex.lock();
	while(true)
	{
		mhm[hashcode%_max_hash]->traverseMysqlHandle(exec);
		if(exec._handle)
		{
                        _mutex.unlock();
			return exec._handle;
		}
		if(mhm[hashcode%_max_hash]->getSize() < _max_handle)
		{
			mysql_url *mu = mum.getMysqlUrlByID(hashcode);
			if(mu)
			{
				mysql_handle *handle = new mysql_handle(mu,this,hashcode%_max_hash);
				if(!handle)
				{
					_mutex.unlock();
					return NULL;
				}
				if(!handle->initHandle())
				{
					SAFE_DELETE(handle);
					_mutex.unlock();
					return NULL;
				}
				if(!mhm[hashcode]->addMysqlHandle(handle))
				{
					_mutex.unlock();
					return NULL;
				}
				tm[hashcode]->reloadAllTables(handle->getMysql());
				_mutex.unlock();
				return handle;
			}
		}
		thread_base::msleep(50);
	}
	_mutex.unlock();
	return exec._handle;
}


2、条件查询

(1)条件查询封装

mysql_record_set* mysql_handle::exeSelect(const char *tablename, mysql_record* column, mysql_record* where, mysql_record* order, 
				unsigned int limit_max,unsigned int limit_min,mysql_record* groupby, mysql_record* having)
{
	mysql_table* table  = this->pool->tm[this->_hashcode]->getTableByName(tablename);//获取表
	if (NULL == _mysql)
	{
		error_log("执行%s时_mysql指针为空",__FUNCTION__);
		return NULL;
	}
	if (NULL == table)
	{
		error_log("执行%s时table指针为空,找不到%s表",__FUNCTION__,tablename);
		return NULL;
	}
	
	_select_time.now();//记录查询时间

	std::ostringstream query_string;
	query_string << "SELECT ";
        //所有字段名
	if(column && !column->isEmpty())
	{
		struct SqlFieldExec : public callback<mysql_field>
		{
			bool _first;
			mysql_table* _table;
			std::ostringstream &query_string;
			SqlFieldExec(mysql_table* table, std::ostringstream &query_string)
				: _first(true), _table(table), query_string(query_string) 
				{
				}
			bool invoke(mysql_field *entry)
			{
				if( entry->name == "*" || _table->fs.get(entry->name))//获取字段
				{
					if(_first)
					{
						_first=false;
					}
					else
					{
						query_string <<  " , ";
					}

					query_string << "`" << entry->name << "`";//字段名
				}
				return true;
			}
		} sfe(table, query_string);

		column->traverseField(sfe);
	}
	else
	{
		query_string << " * ";
	}

	query_string << "  FROM " << " `" << table->name << "` ";
	getWhere(table, query_string, where);//where 条件
	getGroupBy(table, query_string, groupby);//group 
	getHaving(table, query_string, having);//having 条件
	getOrder(table, query_string, order);

	if(limit_max)
	{
		query_string << " LIMIT " << limit_min << " , " << limit_max;
	}
	if(execSql(query_string.str().c_str(), query_string.str().size())) //执行sql
	{
		error_log("%s", mysql_error(_mysql));
		return NULL;
	} 
	unsigned int retCount=0;
	MYSQL_RES *result=NULL;

	result=mysql_store_result(_mysql);//一次性传送结果
	if(result==NULL)
	{
		error_log("%s", mysql_error(_mysql));
		return NULL;
	}
	retCount =mysql_num_rows(result);//获取行数

	if(retCount==0)
	{
		mysql_free_result(result);
		return NULL;
	}

	MYSQL_ROW row;
	unsigned int num_field = mysql_num_fields(result);//获取结果集中的字段数
	MYSQL_FIELD* mysql_fields = NULL;
	mysql_fields = mysql_fetch_fields(result);//获取结果字段

	mysql_record_set* ret_set = NULL;
	ret_set = new mysql_record_set(retCount);
	if(ret_set)
	{
		unsigned int j = 0;
		while((row = mysql_fetch_row(result)))//获取一行结果
		{
			unsigned long *lengths= mysql_fetch_lengths(result);//获取行长度
			mysql_record* rec = ret_set->get(j++);
			if(rec)
			{
				for(unsigned int i=0; i<num_field; i++)
				{
					if(row[i] != NULL)
					{
						//g_log->debug("%s,%s,%lu", mysql_fields[i].name, row[i], lengths[i]);
						rec->put(mysql_fields[i].name, row[i], lengths[i]);//放入结果(字段名、结果、长度)
					}
				}
			}
		}
	}
	mysql_free_result(result);
	if(ret_set && ret_set->size() == 0)
	{
		SAFE_DELETE(ret_set);
	}
	if(_select_time.elapse(realtime()) >= 3 * 1000L)//检查查询事件
	{
		g_log->warn("超时%llu毫秒sql:%s",_select_time.elapse(realtime()),query_string.str().c_str());
	}
	return ret_set;
}

(2)执行sql

int mysql_handle::execSql(const char *sql,unsigned int sqllen,bool need_errlog)
{
	if (NULL == _mysql)
	{
		error_log("执行%s时_mysql指针为空,sql语句为%s",__FUNCTION__,sql);
		return -1;
	}
	if (NULL == sql)
	{
		error_log("执行%s时传入sql语句为空",__FUNCTION__);
		return -1;
	}
	if (0 == sqllen)
	{
		error_log("执行%s时sql语句%s长度为0",__FUNCTION__,sql);
		return -1;
	}

	lastSql=sql;
	int ret=mysql_real_query(_mysql,sql,sqllen);
	if(ret && need_errlog)
	{   
		error_log("%s", mysql_error(_mysql));
		error_log("%s", sql);
	}
	return ret;
}

(3)查询使用实例

查询服务器列表

mysql_record_set *recordset = NULL;
std::ostringstream oss;
mysql_record column,where;
mysql_handle *handle = mysqlPool->getHandle();//从句柄池获取句柄
if (!handle)
{
	g_log->error("不能从数据库连接池获取连接句柄");
	return false;
}
oss << "type=" << CENTERSERVER;
where.put("type",oss.str());//从服务器表格中查询类型为中心服务器的服务器配置
recordset = handle->exeSelect("SERVERLIST",NULL,&where);
if(recordset && recordset->size() == 1)//有结果且结果只有一条
{
	//只有一条满足条件的记录
	if (strcmp(pstrIP, recordset->get(0)->getvalue("ip")) == 0)//从结果集的第一条结果中获取字段为“ip”的值,且比较ip跟本服务器ip一致
	{
		wdServerID = recordset->get(0)->getvalue("id");//获取服务器id、名字、端口
                this->name = (const char* )(recordset->get(0)->getvalue("name"));
		wdPort = recordset->get(0)->getvalue("port");
		wdExtPort = recordset->get(0)->getvalue("extport");
	}
	else
	{
		char temp[MAX_NAME_LEN];//结果不一致,输出日志
		strcpy(temp, recordset->get(0)->getvalue("name"));
		g_log->error("数据库中的记录不符合:%s, %s, %s", pstrIP, (const char *)recordset->get(0)->getvalue("ip"),(const char *)recordset->get(0)->getvalue("name"));
		SAFE_DELETE(recordset);
		mysqlPool->putHandle(handle);
		return false;
	}
}
















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值