游戏服务器之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个数
 */

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mysql_handle_pool::mysql_handle_pool(int max_hash,int max_handle)//int max_hash = 3,int max_handle = 64  
  2. {  
  3.     for(int i = 0 ; i < max_hash ; i ++)  
  4.     {  
  5.         mhm.push_back(new mysql_handle_manager);  
  6.         tm.push_back(new mysql_table_manager);  
  7.     }  
  8.     _max_hash = max_hash;  
  9.     _max_handle = max_handle;  
  10.     g_log->info("Version of the mysql libs is %s" ,mysql_get_client_info());  
  11.     if(!mysql_thread_safe())  
  12.     {         
  13.         g_log->warn("The mysql libs is not thread safe...");  
  14.     }    
  15. }  

(2)加载连接地址

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

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_handle_pool::putUrl(unsigned int hashcode,const char *url)  
  2. {  
  3.     mysql_url *mu = new mysql_url(hashcode,url);//生成连接句柄  
  4.     if(!mu)  
  5.     {  
  6.         return false;  
  7.     }  
  8.     if(!mum.addMysqlUrl(mu))  
  9.     {  
  10.         SAFE_DELETE(mu);  
  11.         return false;  
  12.     }  
  13.     mysql_handle *handle = new mysql_handle(mu,this,hashcode%_max_hash);  
  14.     if(!handle)  
  15.     {  
  16.         return false;  
  17.     }  
  18.     if(!handle->initHandle())//初始化mysql连接  
  19.     {  
  20.         SAFE_DELETE(handle);  
  21.         return false;  
  22.     }  
  23.     if(!mhm[hashcode]->addMysqlHandle(handle))  
  24.     {  
  25.         return false;  
  26.     }  
  27.     tm[hashcode]->reloadAllTables(handle->getMysql());//加载连接上的所有表格  
  28.     return true;  
  29. }  

使用实例

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mysqlPool = new mysql_handle_pool;  
  2. if(mysqlPool == NULL ||!mysqlPool->putUrl(0,g_xml_config.get_string("Global.MYSQL.Config")))  
  3. {  
  4.     g_log->error("连接数据库失败");  
  5.     return false;  
  6. }  

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


(2-1)加载表格

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_table_manager::reloadAllTables(MYSQL *mysql_conn)  
  2. {  
  3.     MYSQL_RES* table_res = NULL;  
  4.   
  5.     if((table_res = mysql_list_tables(mysql_conn, NULL)) == NULL)//加载所有表格  
  6.     {  
  7.         g_log->error("%s:mysql_list_tables fail.", __FUNCTION__);  
  8.         g_log->error("%s:reason: %s", __FUNCTION__,mysql_error(mysql_conn));  
  9.         return false;  
  10.     }             
  11.     MYSQL_ROW row;  
  12.   
  13.     delete_all();  
  14.   
  15.     while((row=mysql_fetch_row(table_res)))  
  16.     {  
  17.         this->addNewTable(mysql_conn, row[0]);  
  18.     }  
  19.   
  20.     mysql_free_result(table_res);  
  21.     return true;  
  22. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_table_manager::addNewTable(MYSQL *mysql_conn,const char *tablename)  
  2. {  
  3.     mysql_table *table = new mysql_table;  
  4.     if(!table)  
  5.     {  
  6.         return false;  
  7.     }  
  8.     strncpy(table->name,tablename,sizeof(table->name));  
  9.     table->reloadAllFields(mysql_conn , tablename);  
  10.     return addTable(table);  
  11. }  


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool addTable(mysql_table *entry)  
  2. {  
  3.     int len = strlen(entry->name);  
  4.     for (int i = 0; i < len; i++)  
  5.     {  
  6.         entry->name[i] = ::toupper(entry->name[i]);  
  7.     }  
  8.       
  9.     bool bret = false;  
  10.     rwlock.wrlock();  
  11.     bret = add_object(entry);  
  12.     rwlock.unlock();  
  13.     return bret;  
  14. }  


加载字段

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_table::reloadAllFields(MYSQL *mysql_conn,const char *tablename)  
  2. {  
  3.     MYSQL_RES* field_res = NULL;  
  4.     field_res = mysql_list_fields(mysql_conn, tablename, NULL);  
  5.     if(!field_res)  
  6.     {  
  7.         return false;  
  8.     }  
  9.     unsigned int num_fields = mysql_num_fields(field_res);  
  10.     MYSQL_FIELD* mysql_fields = NULL;  
  11.     mysql_fields = mysql_fetch_fields(field_res);  
  12.   
  13.     for(unsigned int i=0; i<num_fields; i++)  
  14.     {  
  15.         if(!fs.addNewField(mysql_fields[i].name, mysql_fields[i].type))  
  16.         {  
  17.             mysql_free_result(field_res);  
  18.             return false;  
  19.         }  
  20.     }  
  21.   
  22.     mysql_free_result(field_res);  
  23.     return true;  
  24. }  
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_record::addNewField(const char *fieldname, int fieldType, uint32 mask)  
  2. {  
  3.     mysql_field *entry = new mysql_field;  
  4.     if(!entry)  
  5.     {  
  6.         return false;  
  7.     }  
  8.     strncpy(entry->name,fieldname,sizeof(entry->name));  
  9.     entry->type = fieldType;  
  10.     entry->mask = mask;  
  11.     return addField(entry);  
  12. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool mysql_record::addField(mysql_field *entry)  
  2. {  
  3.     for(unsigned int i = 0;entry->name[i]&&i < sizeof(entry->name);++i)  
  4.         entry->name[i] = (char)::toupper(entry->name[i]);  
  5.         g_log->trace("%s,%u, %s", __PRETTY_FUNCTION__, __LINE__, entry->name.c_str());  
  6.     bool bret = false;  
  7.     rwlock.wrlock();  
  8.     bret = add_object(entry);  
  9.     rwlock.unlock();  
  10.     return bret;  
  11. }  


(3)获取句柄

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mysql_handle *mysql_handle_pool::getHandle(uint32 hashcode)  
  2. {  
  3.     struct GetHandleExec :public callback<mysql_handle>  
  4.     {  
  5.         GetHandleExec():_handle(NULL)  
  6.         {  
  7.         }  
  8.         mysql_handle *_handle;  
  9.         bool invoke(mysql_handle *entry)  
  10.         {  
  11.             switch(entry->state)  
  12.             {  
  13.                 case HandleState_Valid:  
  14.                 case HandleState_Invalid:  
  15.                     {  
  16.                         if(entry->setHandle())//设置句柄  
  17.                         {  
  18.                             _handle = entry;  
  19.                             return false;  
  20.                         }  
  21.                     }  
  22.                     break;  
  23.                 case HandleState_Used:  
  24.                     {  
  25.                         entry->checkUseTime();//检查使用句柄时间  
  26.                     }  
  27.                     break;  
  28.             }  
  29.             return true;  
  30.         }  
  31.     };  
  32.     GetHandleExec exec;  
  33.         _mutex.lock();  
  34.     while(true)  
  35.     {  
  36.         mhm[hashcode%_max_hash]->traverseMysqlHandle(exec);  
  37.         if(exec._handle)  
  38.         {  
  39.                         _mutex.unlock();  
  40.             return exec._handle;  
  41.         }  
  42.         if(mhm[hashcode%_max_hash]->getSize() < _max_handle)  
  43.         {  
  44.             mysql_url *mu = mum.getMysqlUrlByID(hashcode);  
  45.             if(mu)  
  46.             {  
  47.                 mysql_handle *handle = new mysql_handle(mu,this,hashcode%_max_hash);  
  48.                 if(!handle)  
  49.                 {  
  50.                                         _mutex.unlock();  
  51.                     return NULL;  
  52.                 }  
  53.                 if(!handle->initHandle())  
  54.                 {  
  55.                     SAFE_DELETE(handle);  
  56.                                         _mutex.unlock();  
  57.                     return NULL;  
  58.                 }  
  59.                 if(!mhm[hashcode]->addMysqlHandle(handle))  
  60.                 {  
  61.                                          _mutex.unlock();  
  62.                     return NULL;  
  63.                 }  
  64.                 tm[hashcode]->reloadAllTables(handle->getMysql());  
  65.                                 _mutex.unlock();  
  66.                 return handle;  
  67.             }  
  68.         }  
  69.         thread_base::msleep(50);  
  70.     }  
  71.         _mutex.unlock();  
  72.     return exec._handle;  
  73. }  



2、条件查询

(1)条件查询封装

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mysql_record_set* mysql_handle::exeSelect(const char *tablename, mysql_record* column, mysql_record* where, mysql_record* order,   
  2.                 unsigned int limit_max,unsigned int limit_min,mysql_record* groupby, mysql_record* having)  
  3. {  
  4.     mysql_table* table  = this->pool->tm[this->_hashcode]->getTableByName(tablename);//获取表  
  5.     if (NULL == _mysql)  
  6.     {  
  7.         error_log("执行%s时_mysql指针为空",__FUNCTION__);  
  8.         return NULL;  
  9.     }  
  10.     if (NULL == table)  
  11.     {  
  12.         error_log("执行%s时table指针为空,找不到%s表",__FUNCTION__,tablename);  
  13.         return NULL;  
  14.     }  
  15.       
  16.     _select_time.now();//记录查询时间  
  17.   
  18.     std::ostringstream query_string;  
  19.   
  20.     query_string << "SELECT ";  
  21.         //所有字段名  
  22.     if(column && !column->isEmpty())  
  23.     {  
  24.         struct SqlFieldExec : public callback<mysql_field>  
  25.         {  
  26.             bool _first;  
  27.             mysql_table* _table;  
  28.             std::ostringstream &query_string;  
  29.             SqlFieldExec(mysql_table* table, std::ostringstream &query_string)  
  30.                 : _first(true), _table(table), query_string(query_string)   
  31.                 {  
  32.                 }  
  33.             bool invoke(mysql_field *entry)  
  34.             {  
  35.                 if( entry->name == "*" || _table->fs.get(entry->name))//获取字段  
  36.                 {  
  37.                     if(_first)  
  38.                     {  
  39.                         _first=false;  
  40.                     }  
  41.                     else  
  42.                     {  
  43.                         query_string <<  " , ";  
  44.                     }  
  45.   
  46.                     query_string << "`" << entry->name << "`";//字段名  
  47.                 }  
  48.                 return true;  
  49.             }  
  50.         } sfe(table, query_string);  
  51.   
  52.         column->traverseField(sfe);  
  53.     }  
  54.     else  
  55.     {  
  56.         query_string << " * ";  
  57.     }  
  58.   
  59.     query_string << "  FROM " << " `" << table->name << "` ";  
  60.     getWhere(table, query_string, where);//where 条件  
  61.     getGroupBy(table, query_string, groupby);//group   
  62.     getHaving(table, query_string, having);//having 条件  
  63.     getOrder(table, query_string, order);  
  64.   
  65.     if(limit_max)  
  66.     {  
  67.         query_string << " LIMIT " << limit_min << " , " << limit_max;  
  68.     }  
  69.   
  70.     if(execSql(query_string.str().c_str(), query_string.str().size())) //执行sql  
  71.     {  
  72.         error_log("%s", mysql_error(_mysql));  
  73.         return NULL;  
  74.     }   
  75.   
  76.     unsigned int retCount=0;  
  77.     MYSQL_RES *result=NULL;  
  78.   
  79.     result=mysql_store_result(_mysql);//一次性传送结果  
  80.     if(result==NULL)  
  81.     {  
  82.         error_log("%s", mysql_error(_mysql));  
  83.         return NULL;  
  84.     }  
  85.     retCount =mysql_num_rows(result);//获取行数  
  86.   
  87.     if(retCount==0)  
  88.     {  
  89.         mysql_free_result(result);  
  90.         return NULL;  
  91.     }  
  92.   
  93.   
  94.     MYSQL_ROW row;  
  95.     unsigned int num_field = mysql_num_fields(result);//获取结果集中的字段数  
  96.     MYSQL_FIELD* mysql_fields = NULL;  
  97.     mysql_fields = mysql_fetch_fields(result);//获取结果字段  
  98.   
  99.   
  100.     mysql_record_set* ret_set = NULL;  
  101.     ret_set = new mysql_record_set(retCount);  
  102.   
  103.     if(ret_set)  
  104.     {  
  105.         unsigned int j = 0;  
  106.         while((row = mysql_fetch_row(result)))//获取一行结果  
  107.         {  
  108.             unsigned long *lengths= mysql_fetch_lengths(result);//获取行长度  
  109.             mysql_record* rec = ret_set->get(j++);  
  110.             if(rec)  
  111.             {  
  112.                 for(unsigned int i=0; i<num_field; i++)  
  113.                 {  
  114.                     if(row[i] != NULL)  
  115.                     {  
  116.                         //g_log->debug("%s,%s,%lu", mysql_fields[i].name, row[i], lengths[i]);  
  117.                         rec->put(mysql_fields[i].name, row[i], lengths[i]);//放入结果(字段名、结果、长度)  
  118.                     }  
  119.                 }  
  120.             }  
  121.         }  
  122.     }  
  123.   
  124.     mysql_free_result(result);  
  125.   
  126.     if(ret_set && ret_set->size() == 0)  
  127.     {  
  128.         SAFE_DELETE(ret_set);  
  129.     }  
  130.   
  131.   
  132.     if(_select_time.elapse(realtime()) >= 3 * 1000L)//检查查询事件  
  133.     {  
  134.         g_log->warn("超时%llu毫秒sql:%s",_select_time.elapse(realtime()),query_string.str().c_str());  
  135.     }  
  136.     return ret_set;  
  137. }  


(2)执行sql

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int mysql_handle::execSql(const char *sql,unsigned int sqllen,bool need_errlog)  
  2. {  
  3.     if (NULL == _mysql)  
  4.     {  
  5.         error_log("执行%s时_mysql指针为空,sql语句为%s",__FUNCTION__,sql);  
  6.         return -1;  
  7.     }  
  8.     if (NULL == sql)  
  9.     {  
  10.         error_log("执行%s时传入sql语句为空",__FUNCTION__);  
  11.         return -1;  
  12.     }  
  13.     if (0 == sqllen)  
  14.     {  
  15.         error_log("执行%s时sql语句%s长度为0",__FUNCTION__,sql);  
  16.         return -1;  
  17.     }  
  18.   
  19.     lastSql=sql;  
  20.     int ret=mysql_real_query(_mysql,sql,sqllen);  
  21.     if(ret && need_errlog)  
  22.     {     
  23.         error_log("%s", mysql_error(_mysql));  
  24.         error_log("%s", sql);  
  25.     }  
  26.     return ret;  
  27. }  


(3)查询使用实例

查询服务器列表

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mysql_record_set *recordset = NULL;  
  2. std::ostringstream oss;  
  3. mysql_record column,where;  
  4. mysql_handle *handle = mysqlPool->getHandle();//从句柄池获取句柄  
  5. if (!handle)  
  6. {  
  7.     g_log->error("不能从数据库连接池获取连接句柄");  
  8.     return false;  
  9. }  
  10. oss << "type=" << CENTERSERVER;  
  11. where.put("type",oss.str());//从服务器表格中查询类型为中心服务器的服务器配置  
  12. recordset = handle->exeSelect("SERVERLIST",NULL,&where);  
  13. if(recordset && recordset->size() == 1)//有结果且结果只有一条  
  14. {  
  15.     //只有一条满足条件的记录  
  16.     if (strcmp(pstrIP, recordset->get(0)->getvalue("ip")) == 0)//从结果集的第一条结果中获取字段为“ip”的值,且比较ip跟本服务器ip一致  
  17.     {  
  18.         wdServerID = recordset->get(0)->getvalue("id");//获取服务器id、名字、端口  
  19.                 this->name = (const char* )(recordset->get(0)->getvalue("name"));  
  20.         wdPort = recordset->get(0)->getvalue("port");  
  21.         wdExtPort = recordset->get(0)->getvalue("extport");  
  22.     }  
  23.     else  
  24.     {  
  25.         char temp[MAX_NAME_LEN];//结果不一致,输出日志  
  26.         strcpy(temp, recordset->get(0)->getvalue("name"));  
  27.         g_log->error("数据库中的记录不符合:%s, %s, %s", pstrIP, (const char *)recordset->get(0)->getvalue("ip"),(const char *)recordset->get(0)->getvalue("name"));  
  28.         SAFE_DELETE(recordset);  
  29.         mysqlPool->putHandle(handle);  
  30.         return false;  
  31.     }  
  32. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值