网游内存数据库的设计.docx

上传人:b****5 文档编号:30214739 上传时间:2023-08-07 格式:DOCX 页数:17 大小:20.60KB
下载 相关 举报
网游内存数据库的设计.docx_第1页
第1页 / 共17页
网游内存数据库的设计.docx_第2页
第2页 / 共17页
网游内存数据库的设计.docx_第3页
第3页 / 共17页
网游内存数据库的设计.docx_第4页
第4页 / 共17页
网游内存数据库的设计.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

网游内存数据库的设计.docx

《网游内存数据库的设计.docx》由会员分享,可在线阅读,更多相关《网游内存数据库的设计.docx(17页珍藏版)》请在冰豆网上搜索。

网游内存数据库的设计.docx

网游内存数据库的设计

网络游戏的数据变动比较频繁,如果每次数据变动都刷往后端数据库,会导致数据库不负重荷。

在游戏逻辑和数据库间提供一层缓冲服务,有利于减轻这重压力.

首先,网络游戏的数据在数据库中是以表的形式保存的,每个玩家的数据占用其中的一行或几行.以玩家基本属性为例:

基本表:

chainfo

表结构:

chaid,chaname,hp,mp,maxhp,maxmp...

为此,内存数据库将建立针对行集和行数据的抽象。

为了提高查询的效率,在内存中建立一个大的hash-table,hash-table中只支持两种数据结构:

变长的list和定长

的array.list用以表示集,array表示数据行.根据建立的逻辑索引,数据库中的一个表,在hash-table中可能会存放在多处.以玩家任务表为例:

chaid,missionid...

chaid和missionid一起建立了一个唯一的数据库索引,但可以为它建立两个逻辑索引,chaid和chaid,missionid.

这样,当用户上线时(假设用户id为1001),将导入所有chaid==1001的行,在hash-table中建立一个list,这个list中的每个元素都是一个array,每个array表示一个任

务记录行,list就是这个玩家所有任务的集合,如果游戏逻辑需要获取这个玩家的任务列表,可以通过以下key获取"mission,chaid,1001".当然仅有一个行集是不够的,

因为当用户的某个任务数据变动时,必须遍历list,找到正确的array再将变动更新到正确的array中.而网络游戏中最频繁的就是更新操作了,为了提高效率,为每一个

任务行建立一个逻辑索引"chaid,missionid",将任务对应的数据行也存放在hash-table中,这样如果1001号玩家希望改变他的2号任务的数据,则可以发key="mission,

chaid,missionid,1001,2"后跟变更数据.来改变2号任务的数据.

下面贴出代码片段,介绍核心的存储数据结构.

enum

{

INT8=0,

INT16,

INT32,

INT64,

DOUBLE,

STRING,

BINARY,

};

typedefstructbasetype

{

int8_ttype;//therealtype

void*data;

}*basetype_t;

structdb_type_string

{

structbasetypebase;

int32_tsize;

};

structdb_type_binary

{

structbasetypebase;

int32_tsize;

};

首先是基本的数据元素,也就是array可以存放的元素类型,分别是4种整型,double,字符串和二进制数据.

enum

{

DB_LIST=1,

DB_ARRAY,

};

typedefstructdb_element

{

structrefbaseref;

int32_thash_index;//indexinglobal_table

int8_ttype;

}*db_element_t;

db_element_tdb_element_acquire(db_element_t,db_element_t);

voiddb_element_release(db_element_t*);

 

//representadbrow

typedefstructdb_array

{

structdb_elementbase;

int32_tsize;

basetype_t*data;

}*db_array_t;

 

db_array_tdb_array_create(int32_tsize);

db_array_tdb_array_acquire(db_array_t,db_array_t);

voiddb_array_clear(db_array_t);//clearthedata

voiddb_array_release(db_array_t*);

 

//get/setoneelementofthedbrow

basetype_tdb_array_get(db_array_t,int32_tindex);

voiddb_array_set(db_array_t,int32_tindex,basetype_t);

structdb_node

{

list_nodenext;

db_array_tarray;

};

//representdbrowset

typedefstructdb_list

{

structdb_elementbase;

structlink_list*l;

}*db_list_t;

db_list_tdb_list_create();

db_list_tdb_list_acquire(db_list_t,db_list_t);

voiddb_list_release(db_list_t*);

int32_tdb_list_append(db_list_t,db_array_t);

int32_tdb_list_size(db_list_t);

int32_tdb_list_shrink(db_list_t);

然后是array和list的定义,他们都继承自db_element_t,而hash_table中的元素正是db_element_t.array和list都实现了引用计数,这样当所有引用都释放时,可以被正

确的销毁。

这里要注意的是,一个array可能被存放在多个list中,这样,当一个数据行被删除时,必须让所有的list知道这个数据已经无效。

我的做法不是在array被删

除时通知所有的list删除对应的array,而是通过db_array_clear,清除array中存放的有效数据。

然后通过一个算法,定期对数据占用最多的list执行shrink,以销毁失效的

array.

typedefstructglobal_table*global_table_t;

global_table_tglobal_table_create(int32_tinitsize);

voidglobal_table_destroy(global_table_t*);

 

db_element_tglobal_table_find(global_table_t,constchar*key);

int32_tglobal_table_remove(global_table_t,constchar*key);

int32_tglobal_table_add(global_table_t,constchar*key,db_element_te);

//collectunuseddb_element_t

voidglobal_table_shrink(global_table_t);

然后是hash_table的定义,只向外提供三个操作接口,分别是查找,删除和添加.对于添加操作,如果最开始往一个hashslot添加的是一个array,当再次往这个slot添加

一个array时,将会自动将slot中的元素从array提升为list,并将两个array都添加到这个list中.

下面是一些测试代码:

#include

#include"global_table.h"

 

intmain()

{

global_table_tgtb=global_table_create(1024);

db_array_ta1=db_array_create(3);

db_array_ta2=db_array_create(3);

db_array_ta3=db_array_create(3);

db_array_ta4=db_array_create(3);

db_array_set(a1,0,basetype_create_int32(10));

db_array_set(a1,1,basetype_create_int32(11));

db_array_set(a1,2,basetype_create_int32(12));

db_array_set(a2,0,basetype_create_int32(20));

db_array_set(a2,1,basetype_create_int32(21));

db_array_set(a2,2,basetype_create_int32(22));

db_array_set(a3,0,basetype_create_int32(30));

db_array_set(a3,1,basetype_create_int32(31));

db_array_set(a3,2,basetype_create_int32(32));

db_array_set(a4,0,basetype_create_int32(40));

db_array_set(a4,1,basetype_create_int32(41));

db_array_set(a4,2,basetype_create_int32(42));

global_table_add(gtb,"kenny",(db_element_t)a1);

global_table_add(gtb,"kenny",(db_element_t)a2);

global_table_add(gtb,"kenny",(db_element_t)a3);

global_table_add(gtb,"kenny",(db_element_t)a4);

global_table_add(gtb,"kenny1",(db_element_t)a1);

global_table_add(gtb,"kenny2",(db_element_t)a2);

global_table_add(gtb,"kenny3",(db_element_t)a3);

global_table_add(gtb,"kenny4",(db_element_t)a4);

//testsearch

db_list_tl=(db_list_t)global_table_find(gtb,"kenny");

printf("therowsizeofkenny(adb_list_t):

%d\n",db_list_size(l));

printf("elementofa1:

key(kenny1):

");

db_array_t_a=(db_array_t)global_table_find(gtb,"kenny1");

inti=0;

for(;i<3;++i)

{

basetype_tb=db_array_get(_a,i);

printf("%d",basetype_get_int32(b));

}

printf("\n");

printf("elementofa2:

key(kenny2):

");

_a=(db_array_t)global_table_find(gtb,"kenny2");

i=0;

for(;i<3;++i)

{

basetype_tb=db_array_get(_a,i);

printf("%d",basetype_get_int32(b));

}

printf("\n");

printf("elementofa3:

key(kenny3):

");

_a=(db_array_t)global_table_find(gtb,"kenny3");

i=0;

for(;i<3;++i)

{

basetype_tb=db_array_get(_a,i);

printf("%d",basetype_get_int32(b));

}

printf("\n");

printf("elementofa4:

key(kenny4):

");

_a=(db_array_t)global_table_find(gtb,"kenny4");

i=0;

for(;i<3;++i)

{

basetype_tb=db_array_get(_a,i);

printf("%d",basetype_get_int32(b));

}

printf("\n");

db_array_release(&a4);

global_table_remove(gtb,"kenny4");

/*shrinkwillcausetherefcountofa4reducetozero,

*thena4willbedestroyed

*/

db_list_shrink(l);

printf("therowsizeofkenny(adb_list_t),afterremoveandshrink:

%d\n",db_list_size(l));

db_array_release(&a1);

db_array_release(&a2);

db_array_release(&a3);

printf("destroyglobaltable,thiswillcauseallelementdestroyed\n");

global_table_destroy(>b);

return0;

}

本篇主要介绍一个测试前端,以及测试的远程调用协议.

先贴出测试前端的服务器代码:

#include"netservice.h"

#include"msg_loop.h"

#include"datasocket.h"

#include"SysTime.h"

#include"db_protocal.h"

atomic_32_twpacket_count=0;

atomic_32_trpacket_count=0;

atomic_32_tbuf_count=0;

global_table_tgtb;

voidserver_process_packet(datasocket_ts,rpacket_tr)

{

//执行操作并返回结果

cache_protocal_tp;

uint32_tcoro_id=rpacket_read_uint32(r);

uint8_ttype=rpacket_read_uint8(r);

switch(type)

{

caseCACHE_GET:

p=create_get();

break;

caseCACHE_SET:

p=create_set();

break;

caseCACHE_DEL:

p=create_del();

break;

}

wpacket_tret=p->execute(gtb,r,coro_id);

if(NULL!

=ret)

data_send(s,ret);

destroy_protocal(&p);

}

voidprocess_new_connection(datasocket_ts)

{

printf("w:

%u,r:

%u,b:

%u\n",wpacket_count,rpacket_count,buf_count);

}

voidprocess_connection_disconnect(datasocket_ts,int32_treason)

{

release_datasocket(&s);

printf("w:

%u,r:

%u,b:

%u\n",wpacket_count,rpacket_count,buf_count);

}

voidprocess_send_block(datasocket_ts)

{

//发送阻塞,直接关闭

close_datasocket(s);

}

 

constchar*ip;

uint32_tport;

intmain(intargc,char**argv)

{

init_net_service();

ip=argv[1];

port=atoi(argv[2]);

netservice_tn=create_net_service

(1);

gtb=global_table_create(65536);

int32_ti=0;

charkey[64];

for(;i<1000000;++i)

{

basetype_ta=basetype_create_int32(i);

snprintf(key,64,"test%d",i);

a=global_table_insert(gtb,key,a,global_hash(key));

if(!

a)

printf("error1\n");

basetype_release(&a);

}

net_add_listener(n,ip,port);

msg_loop_tm=create_msg_loop(server_process_packet,process_new_connection,process_connection_disconnect,process_send_block);

while

(1)

{

msg_loop_once(m,n,100);

}

return0;

}

前端的网络模块使用了在上一篇中介绍的网络框架,启动时先插入100W条32位整型的记录,然后进入消息循环,不断的处理从客户端发过来的操作请求.

目前只添加了三个协议,分别是获取:

CACHE_GET;添加/修改:

CACHE_SET;删除:

CACHE_DEL.

服务器处理协议并将结果返回给客户端.

然后是测试客户端:

#include"db_protocal.h"

#include"dbtype.h"

#include

#include"SocketWrapper.h"

#include"SysTime.h"

#include"KendyNet.h"

#include"Connector.h"

#include"Connection.h"

#include"common_define.h"

#include"netservice.h"

#include"msg_loop.h"

#include"co_sche.h"

sche_tg_sche=NULL;

uint32_tcall_count=0;

atomic_32_twpacket_count=0;

atomic_32_trpacket_count=0;

atomic_32_tbuf_count=0;

datasocket_tdb_s;

int8_ttest_select(constchar*key,int32_ti)

{

coro_tco=get_current_coro();

wpacket_twpk=get_wpacket(64);

wpacket_write_uint32(wpk,(int32_t)co);

wpacket_write_uint8(wpk,CACHE_GET);//ÉèÖÃ

wpacket_write_string(wpk,key);

data_send(db_s,wpk);

coro_block(co);

int8_tret=rpacket_read_uint8(co->rpc_response);

rpacket_read_uint8(co->rpc_response);

int32_tval=rpacket_read_uint32(co->rpc_response);

if(val!

=i)

printf("error\n");

//printf("begin\n");

rpacket_destroy(&co->rpc_response);

//printf("end\n");

returnret;

}

void*test_coro_fun2(void*arg)

{

coro_tco=get_current_coro();

while

(1)

{

charkey[64];

int32_ti=rand()%1000000;

snprintf(key,64,"test%d",100);

if(0==test_select(key,100))

++call_count;

}

}

 

voidserver_process_packet(datasocket_ts,rpacket_tr)

{

coro_tco=(coro_t)rpacket_read_uint32(r);

co->rpc_response=rpacket_create_by_rpacket(r);

coro_wakeup(co);

}

voidprocess_new_connection(datasocket_ts)

{

printf("connectserver\n");

db_s=s;

g_sche=sche_create(20000,65536,NULL,NULL);

inti=0;

for(;i<20000;++i)

{

sche_spawn(g_sche,test_coro_fun2,NULL);

}

}

voidprocess_connection_disconnect(datasocket_ts,int32_treason)

{

release_datasocket(&s);

}

voidprocess_send_block(datasocket_ts)

{

//·¢ËÍ×èÈû,Ö±½Ó¹Ø±Õ

close_datasocket(s);

}

intmain(intargc,char**argv)

{

init_net_service();

constchar*ip=argv[1];

uint

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 金融投资

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1