浅谈网络游戏内存数据库的设计(1)

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

基本表: 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号任务的数据.

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

  1. enum 
  2.     INT8 = 0, 
  3.     INT16, 
  4.     INT32, 
  5.     INT64, 
  6.     DOUBLE, 
  7.     STRING, 
  8.     BINARY, 
  9. }; 
  10.  
  11. typedef struct basetype 
  12.     int8_t type;//the real type 
  13.     void  *data; 
  14. }*basetype_t; 
  15.  
  16. struct db_type_string 
  17.     struct basetype base
  18.     int32_t size; 
  19. }; 
  20.  
  21. struct db_type_binary 
  22.     struct basetype base
  23.     int32_t size; 
  24. }; 

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

  1. enum 
  2.     DB_LIST = 1, 
  3.     DB_ARRAY, 
  4. }; 
  5.  
  6. typedef struct db_element 
  7.     struct refbase ref
  8.     int32_t hash_index;//index in global_table 
  9.     int8_t type; 
  10. }*db_element_t; 
  11.  
  12. db_element_t db_element_acquire(db_element_t,db_element_t); 
  13. void db_element_release(db_element_t*); 
  14.  
  15.  
  16. //represent a db row 
  17. typedef struct db_array 
  18.     struct db_element base
  19.     int32_t     size; 
  20.     basetype_t* data;  
  21. }*db_array_t; 
  22.  
  23.  
  24. db_array_t db_array_create(int32_t size); 
  25. db_array_t db_array_acquire(db_array_t,db_array_t); 
  26. void       db_array_clear(db_array_t);//clear the data 
  27. void       db_array_release(db_array_t*); 
  28.  
  29.  
  30. //get/set one element of the db row 
  31. basetype_t db_array_get(db_array_t,int32_t index); 
  32. void       db_array_set(db_array_t,int32_t index,basetype_t); 
  33.  
  34. struct db_node 
  35.     list_node  next; 
  36.     db_array_t array; 
  37. }; 
  38.  
  39. //represent db row set 
  40. typedef struct db_list 
  41.     struct db_element base
  42.     struct link_list *l; 
  43.      
  44. }*db_list_t; 
  45.  
  46. db_list_t db_list_create(); 
  47. db_list_t db_list_acquire(db_list_t,db_list_t); 
  48. void      db_list_release(db_list_t*); 
  49. int32_t   db_list_append(db_list_t,db_array_t); 
  50. int32_t   db_list_size(db_list_t); 
  51. int32_t   db_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.

  1. typedef struct global_table *global_table_t; 
  2.  
  3. global_table_t global_table_create(int32_t initsize); 
  4. void           global_table_destroy(global_table_t*); 
  5.  
  6.  
  7. db_element_t   global_table_find(global_table_t,const char *key); 
  8. int32_t        global_table_remove(global_table_t,const char *key); 
  9. int32_t        global_table_add(global_table_t,const char *key,db_element_t e); 
  10.  
  11. //collect unused db_element_t 
  12. void           global_table_shrink(global_table_t); 

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

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

下面是一些测试代码:

  1. #include <stdio.h> 
  2. #include "global_table.h" 
  3.  
  4.  
  5. int main() 
  6.     global_table_t gtb = global_table_create(1024); 
  7.      
  8.     db_array_t a1 = db_array_create(3); 
  9.     db_array_t a2 = db_array_create(3); 
  10.     db_array_t a3 = db_array_create(3); 
  11.     db_array_t a4 = db_array_create(3); 
  12.      
  13.     db_array_set(a1,0,basetype_create_int32(10)); 
  14.     db_array_set(a1,1,basetype_create_int32(11)); 
  15.     db_array_set(a1,2,basetype_create_int32(12)); 
  16.      
  17.     db_array_set(a2,0,basetype_create_int32(20)); 
  18.     db_array_set(a2,1,basetype_create_int32(21)); 
  19.     db_array_set(a2,2,basetype_create_int32(22)); 
  20.      
  21.     db_array_set(a3,0,basetype_create_int32(30)); 
  22.     db_array_set(a3,1,basetype_create_int32(31)); 
  23.     db_array_set(a3,2,basetype_create_int32(32)); 
  24.      
  25.     db_array_set(a4,0,basetype_create_int32(40)); 
  26.     db_array_set(a4,1,basetype_create_int32(41)); 
  27.     db_array_set(a4,2,basetype_create_int32(42)); 
  28.      
  29.     global_table_add(gtb,"kenny",(db_element_t)a1); 
  30.     global_table_add(gtb,"kenny",(db_element_t)a2); 
  31.     global_table_add(gtb,"kenny",(db_element_t)a3); 
  32.     global_table_add(gtb,"kenny",(db_element_t)a4); 
  33.     global_table_add(gtb,"kenny1",(db_element_t)a1); 
  34.     global_table_add(gtb,"kenny2",(db_element_t)a2); 
  35.     global_table_add(gtb,"kenny3",(db_element_t)a3); 
  36.     global_table_add(gtb,"kenny4",(db_element_t)a4); 
  37.          
  38.          
  39.     //test search     
  40.     db_list_t l = (db_list_t)global_table_find(gtb,"kenny"); 
  41.      
  42.     printf("the row size of kenny(a db_list_t):%d\n",db_list_size(l)); 
  43.      
  44.     printf("element of a1:key(kenny1):"); 
  45.     db_array_t _a = (db_array_t)global_table_find(gtb,"kenny1"); 
  46.     int i = 0; 
  47.     for( ; i < 3; ++i) 
  48.     { 
  49.         basetype_t b = db_array_get(_a,i); 
  50.         printf("%d ",basetype_get_int32(b)); 
  51.     } 
  52.     printf("\n"); 
  53.      
  54.     printf("element of a2:key(kenny2):"); 
  55.     _a = (db_array_t)global_table_find(gtb,"kenny2"); 
  56.     i = 0; 
  57.     for( ; i < 3; ++i) 
  58.     { 
  59.         basetype_t b = db_array_get(_a,i); 
  60.         printf("%d ",basetype_get_int32(b)); 
  61.     } 
  62.      
  63.     printf("\n"); 
  64.      
  65.     printf("element of a3:key(kenny3):"); 
  66.     _a = (db_array_t)global_table_find(gtb,"kenny3"); 
  67.     i = 0; 
  68.     for( ; i < 3; ++i) 
  69.     { 
  70.         basetype_t b = db_array_get(_a,i); 
  71.         printf("%d ",basetype_get_int32(b)); 
  72.     } 
  73.      
  74.     printf("\n"); 
  75.      
  76.     printf("element of a4:key(kenny4):"); 
  77.     _a = (db_array_t)global_table_find(gtb,"kenny4"); 
  78.     i = 0; 
  79.     for( ; i < 3; ++i) 
  80.     { 
  81.         basetype_t b = db_array_get(_a,i); 
  82.         printf("%d ",basetype_get_int32(b)); 
  83.     } 
  84.      
  85.     printf("\n"); 
  86.      
  87.     db_array_release(&a4); 
  88.     global_table_remove(gtb,"kenny4"); 
  89.     /* shrink will cause the refcount of a4 reduce to zero, 
  90.      * then a4 will be destroyed 
  91.     */ 
  92.     db_list_shrink(l); 
  93.      
  94.     printf("the row size of kenny(a db_list_t),after remove and shrink: %d\n",db_list_size(l));         
  95.      
  96.     db_array_release(&a1); 
  97.     db_array_release(&a2); 
  98.     db_array_release(&a3); 
  99.      
  100.      
  101.      
  102.     printf("destroy global table,this will cause all element destroyed\n"); 
  103.     global_table_destroy(&gtb); 
  104.      
  105.     return 0; 

本篇仅仅介绍了核心的数据结构,后端的数据库交互策略,网络前端,备份处理和分布式多缓存将在后面慢慢介绍.

 代码地址:https://github.com/sniperHW/kendylib dbcahce目录

原文链接:http://www.cnblogs.com/sniperHW/archive/2012/08/11/2634052.html

【编辑推荐】

  1. 利用Java进行MySql数据库的导入和导出
  2. MySQL集群CGE电信运营商级版本
  3. MySQL 5.5.21 GA发布(附下载)
  4. MySQL体系结构详解
  5. 超越MySQL 对流行数据库进行分支

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/277347.html<

(0)
管理的头像管理
上一篇2025-05-11 02:10
下一篇 2025-05-11 02:11

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注