云风的 BLOG 开发笔记 10 内存数据库.docx

上传人:b****5 文档编号:30743346 上传时间:2023-08-20 格式:DOCX 页数:8 大小:19.28KB
下载 相关 举报
云风的 BLOG 开发笔记 10 内存数据库.docx_第1页
第1页 / 共8页
云风的 BLOG 开发笔记 10 内存数据库.docx_第2页
第2页 / 共8页
云风的 BLOG 开发笔记 10 内存数据库.docx_第3页
第3页 / 共8页
云风的 BLOG 开发笔记 10 内存数据库.docx_第4页
第4页 / 共8页
云风的 BLOG 开发笔记 10 内存数据库.docx_第5页
第5页 / 共8页
点击查看更多>>
下载资源
资源描述

云风的 BLOG 开发笔记 10 内存数据库.docx

《云风的 BLOG 开发笔记 10 内存数据库.docx》由会员分享,可在线阅读,更多相关《云风的 BLOG 开发笔记 10 内存数据库.docx(8页珍藏版)》请在冰豆网上搜索。

云风的 BLOG 开发笔记 10 内存数据库.docx

云风的BLOG开发笔记10内存数据库

云风的BLOG开发笔记(10):

内存数据库

开发笔记(10):

内存数据库

 

离上一次写开发笔记快有一个月了。

当然,中间我们放了10天的长假。

项目的进展比较缓慢、主要是解决一些琐碎的技术问题,客户端的比较多,服务器这边就是节前的一些bug修改和功能完善。

大部分工作都不是我自己在做。

由于感到人手不足,小规模私下的做了一点点招聘工作。

也算物色到一两个同学可以过来一起干的。

好久没做招聘工作了,都不知道怎么开始谈。

唉,我们这里条件不算好,要求还多,都不好意思开口。

可写的东西其实也不少。

今天挑一点来记录一下。

话要说回开发笔记第六篇,我谈过结构化数据的共享存储。

这个模块细化其实有挺多工作要做。

核心部分我自己完成了,没有用太多时间。

然后,有位华南理工的同学想来实习。

想到他们学校距离我们办公室仅有十分钟步行距离,我便考虑让logicouter同学过来试试接手这个模块,做后续开发。

当然,他不能全职来做,对旧代码也需要一定时间熟悉,进度比较慢。

我的规划大约是这样的:

核心部分仅仅实现了结构化数据在内存中的表达。

但储存在内存中的数据还不能直接使用。

CAPI虽然实现好了,但性能比较低。

我实现的是无锁的数据结构,采用单向链表保存。

检索一项属性值都是O(n)的复杂度。

这显然是不能在项目中直接使用的。

为了提供性能,需要再做一层cache。

在Lua虚拟机中,用hash表映射。

使得读写数据的速度都降低到O

(1)。

因为我自己对Lua比较熟悉,所以这步Lua的薄封装还是我自己完成的。

实测下来,和原生的Lua表访问差距不到一个数量级(3,4倍左右),是可以接受的范围。

比常规IPC通讯要快的多,也没有异步通讯的负担。

以后编写逻辑代码的时候稍微注意一点就好了。

需要额外开发的工作是,要定义一个数据描述的小语言。

类似C语言的结构定义。

在数据储存中,我没有实现无格式信息的字典类型。

map的key都是在结构定义中预先定义好的,内存中存放的是编号。

这一是因为实现简单,而是可以实现成无锁的数据结构。

再就是数据结构也能严谨一些,减少typo(可以立刻检查到)。

 

一开始我打算直接使用googleprotobuf的协议定义语言。

开始做的时候发现意义不大。

不如自己定义一个更为简单一些的,切合需求。

另外,我们需要更好的多版本支持。

大概的样子是这样的:

typeEtc{

int[]object_id=1

}

enumGender{

male=1

female=2

}

typemain{

stringname=1

intid=2

Gendergender=3

Etcetc=4[deprecated]

}

这只是一个示意,没有定稿。

具体也想让logicouter同学自己来设计决定。

对于版本更替的问题,我想是必然遇到的。

暂时我们采用简单的策略,就是只增加新的域,而不修改老的。

但是可以在一些不再支持的域上面标记deprecated。

有这个标记,我们可以在持久化这些数据的时候做一些处理,并能够逐步淘汰老版本的协议。

Lua层不会直接利用这段文本来分析数据结构,而是从这段文本生成必要的数据结构信息。

它将是一张luatable。

我们可以动态生成并序列化后cache起来。

当然直接用lpeg直接解析这段文本并不困难。

只是,lpeg的内存开销略大,我们的服务器想尽量保持单个luastate的精简,以方便在同一台机器上可以开出上万个luastate。

btw,之前我提到过,我希望尽量把单一的事情放在单一的luastate里去做。

每个luastate处理的业务很小,甚至处理完后可以直接关闭,而不需要承受gc的代价。

lua的bootstrap过程非常之快,调整后,空的luastate占内存非常小(可以减少到10k之下)。

这样,加载lua代码甚至是比较大的一个开销。

因为parser本身是要消耗内存的,至少要多一份文本代码。

你可以把lua文本代码做预编译,但是那样对开发不太友好(流程较多)。

有些代码是动态生成的,也不方便做这个工作(比如利用lpeg解析上述DSL并生成lua表再做序列化)。

如果luastate间通讯做的比较完善的话,你可以在一个独立的luastate中做这些代码动态生成,或是编译加载的工作,dump出bytecode(适当做cache),最后传递给最终的luastate使用。

既然是BTW,那么这些并不是这次工作的重点,只是一个优化方向了。

自定义DSL的好处是,你可以对DSL做解析,对同一段描述做不同的理解。

就拿deprecated来说,就可以有专门负责数据持久化的模块分析出来做妥善的数据管理。

而当前的逻辑运算的模块则可以忽略掉这些域。

在最终的应用环境中,我们将严格保证按生产消费模式使用这些数据。

对于玩家数据,是一个单一类型的机构(就是例子中的typemain)。

agent进程attach对应玩家的id,得到共享内存对象。

只有这个agent有权力改写共享对象的内容。

其它所有服务都只能读这个对象的数据,包括其它的agent,场景服务,等等。

其它人对这个对象的修改,都需要用RPC调用通知agent来实现。

共享内存带来的好处是,不需要通过RPC调用来同步对象的内部数据。

而只需要传递id和更改消息即可。

数据持久化工作是额外服务完成的,这个服务是一个特例,它可以读写共享数据体。

但是在它写的过程中,没有任何其它服务可以读(包括agent)。

它负责从外部数据库中读出对象的全部信息,并写入共享数据体,然后通知agent准备好了。

或是另一个用来实现离线玩家数据查询的异步API。

根据之前的分析,这个共享数据体是可以很安全的做数据快照的,不需要OS的支持。

只需要顺序遍历数据体即可。

这个工作也是由这个服务一并完成的,可以采用定期将数据写入数据库的这种策略工作。

这样,保存玩家数据的外部数据库到底用什么反而不太重要了。

Redis也好,Mysql也罢,是可以替换的。

重要的是,最后提到的这个玩家数据持久化服务对开发尤其重要。

我们可以把它看成是一个自制的内存数据库系统。

只是可以不通过低效的socket访问。

除了备份和读取外部数据库数据的功能外,不可缺少的是遍历已有数据的相关API。

围绕这些可能需要做一个工具集。

好在我们现在大体的基础设施已经完成,做这些都比较容易了。

姑且把这个叫做内存数据库吧。

如果日后不仅仅想用它来保存玩家数据怎么办?

我觉得有个简单的方法可以实现。

typePlayer{

}

typeNpc{

}

typeScene{

}

typemain{

Playerplayer=1

Npcnpc=2

Scenescene=3

}

我们可以用两层的数据结构解决这个问题,类似googleprotobuf中推荐的union的模拟方案。

我们在内存数据库中的每个对象都按typemain保存。

并仅填写其中的一个域。

以上面的例子为例,要么它有一组Player数据,要么是Scene数据。

在储存空间上不会有太大浪费,也并影响检索效率。

只是在用到的地方,需要了解哪个id具体是什么类型的数据罢了。

云风提交于February14,201201:

14AM|固定链接Comments

 

自从年初在微博上看到云风前辈提到的Redia我就向公司提建议使用这个替代原来用SQLserver做缓存的糟糕机制,也直是到最近才从工程里面脱出身来在MS的基础上把整个Redis给改写重新封装成windows平台下的仿SQLServer……我想我真的是疯了

 

Postedby:

KagamiBoss|(16)

November22,201212:

06AM自从年初在微博上看到云风前辈提到的Redia我就向公司提建议使用这个替代原来用SQLserver做缓存的糟糕机制,也直是到最近才从工程里面脱出身来在MS的基础上把整个Redis给改写重新封装成windows平台下的仿SQLServer……我想我真的是疯了

 

Postedby:

KagamiBoss|(15)

November22,201212:

05AM这个是说进程间的内存共享吧。

如果是这样的,那这样都得部署在同一物理服务器上哦。

 

Postedby:

smalllemon|(14)

February25,201210:

56PM您好云风,你的整个服务器架构是基于服务划分的吧?

 

Postedby:

tinyzhang|(13)

February17,201211:

23AM我把SQLserver放到内存盘中运行。

服务器再加个UPS........哈哈哈,主要是自己才差,写不出来象MSSQL那样方便可以用SQL查询的内存数据库。

Postedby:

ot512|(12)

February15,201202:

50PMenumSex{

male=1

female=2

}

================

Sex是否应该是Gender?

是性别吧

 

Postedby:

skyscribe|(11)

February15,201212:

24PM有没有考虑过直接做无锁关联数组,就可以省掉缓存层。

这种无锁关联数组可以用HAMT实现:

https:

//en.wikipedia.org/wiki/Hash_array_mapped_triePostedby:

Atry|(10)

February14,201207:

16PM云风你好!

follow你的博客一年多了,学到了很多东西,非常谢谢你的知识分享。

从你的博客看来,似乎你很强调自主开发底层模块——常常看到有这方面的经验分享。

我很好奇你是如何决定自己开发而不是使用现有开源代码的,比如说,如果一个开源项目能实现你x%的想法,那这个x多少是一个坎呢?

D

我的坎在70%-80%左右。

 

Postedby:

Jacky|(9)

February14,201203:

37PMAmazingwork!

~:

D

 

Postedby:

final|(8)

February14,201202:

25PM学习了,以后可以借鉴一下

 

Postedby:

cffile|(7)

February14,201201:

39PM感觉数据的结构定义很重要的,一般来说的分段吧,比如说有些是不需要同步到客户端的,有些是不需要存数据库的。

些细节的考虑到数据结构定义中

 

Postedby:

smalllemon|(6)

February14,201210:

05AM云风终于又写了,先留言占位,然后再慢慢的看。

 

Postedby:

starshine|(5)

February14,201209:

38AM没有实践过,但对网络部分有点兴趣.

 

Postedby:

shenjian|(4)

February14,201209:

35AM有没有考虑过lua的parse消耗太大而又不适合做预编译的情况下寻找或者自制一个高效的单遍边parse边执行的脚本系统,当然这个脚本的功能可能很有限,但常用的简单要素还是可以支持的比如if-elif-else、循环、宿主接口调用啥的。

这样一部分脚本就能达到strlen级别的消耗。

对于定制脚本不能满足的少量情况也可以在定制脚本代码里面内嵌lua来应付。

 

Postedby:

paladin_t|(3)

February14,201209:

34AM@Freeeyes

写数据永远保证只有agent一个。

数据结构是无锁的。

 

Postedby:

Cloud|

(2)

February14,201202:

30AM看似corba规范,如果存在共享数据的竞争问题的多agent需求?

如何保证效率呢?

天龙八部采用了枽类似的方法,但稍微有些不同。

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

当前位置:首页 > 医药卫生 > 基础医学

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

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