« 12 月二三事 | 返回首页 | 梦幻西游服务器的优化 »

梦幻西游服务器 IO 的一点优化

关注梦幻西游服务器的性能问题,是源于前几天跟同事的聊天。谈到能否把梦幻西游服务器做成无盘站,或是放进虚拟机里,便于日常维护管理。

意外的了解到,现在磁盘 IO 性能居然成了梦幻西游服务器的瓶颈。而不是 CPU 或是网络带宽。据我所知,梦幻西游的服务器数据储存是这样做的:

主游戏进程不负责储存,一切都在内存中。所有玩家的数据就是内存数据结构。只是在玩家登陆的时候去读取一下本地的文本文件,以及登出的时候把数据序列化成文本,然后保存在本地文件中。

为了防止中途发生意外,游戏进程会定期把内存全部数据序列化,然后通过共享内存的方式让另一个 IO 进程不断的把数据保存在磁盘上。

这些都是 10 年前做的设计决策,无论是否合理,都已经稳定运行了很多年了。不少朋友问起,我们的游戏服务用的什么数据库系统,我都只好说,我们没有用数据库,只用了文件系统。面对诧异的目光,我都不想过多解释。好吧,其实我也觉得 SQL 神马的都是浮云。

目前在 8 千人以上同时在线的服务器上,磁盘 IO 非常繁忙,据说已经影响到了正常的游戏。由于长年的修修补补,整个系统已经不是上面提到的那些单纯。还有一些额外的 IO 操作,这些被定期写盘的 IO 操作影响到了。


直觉告诉我,这个环节做优化比较容易,事半功倍。之所以之前没有人做,只是因为很少人愿意去碰那些已经看起来稳定运行了很多年的系统,通过改善硬件就可以缓解的问题。

方法其实很简单。只需要简单部署一个内存 cache ,把所有需要读取的数据都在 cache 里存放起来,而不直接去读文件。这个东西可以找现成的方案,也可以自己写一个(不会太难)。之所以自己来写这个 cache ,是为了下一步方便。

接下来就是,每次定时存盘时,不在保存全部的数据,而只保存跟上一次数据的差异。简单说,就是先做一个 diff ,再保存。定时存盘仅仅是为了危机处理,只要信息都在,其实真没必要保存每个时间点的快照的。这是个通用的方法。找不到合适的通用工具的原因之一在于传统的 diff 软件是针对文本行的。而二进制 diff 的开源方案较少,且算法更复杂一些。

梦幻西游玩家持久化数据虽然是文本的,但没有特别的格式规范,明显带有多年演化的痕迹。有的文本行长达数千字节,简单的基于文本行的 diff 处理,效果不好。

而且总数据量较大。每个玩家的数据在 48K 以下。玩家数量级在 1 万左右。这样,每批数据量在百兆级。如果用独立工具,数据传递本身的开销就比较大了。定制一个服务来处理这个事情代价要小的多。以梦幻西游这个每月给网易带来上亿收入的产品来说。开发这种小程序的成本几乎可以忽略不计,需要的只是稳定可靠。

这两天我简单写了几百行 C 程序,实现了个简单的 diff ,没有怎么优化,只是把传统的 diff 中的回车分段改成了更多的可定义的分割符。把每个玩家几十 K 的文本数据块,分割成了 2000 来个数据元,用传统 diff 算法处理。

性能还可以接受,一秒可以处理 20 组玩家数据。玩家平均游戏时间半小时间的 diff 量是总数据量的 10% 左右。现在我们标准配置 8 核的服务器,通常都会闲置几个 CPU 出来,正好用来计算 diff 。减少 90% 的磁盘 IO 量(如果加上压缩,将更客观),优化后的效果将非常明显。

Comments

服务器是很难优化。
这个优化手段不错
呵呵,现在都不玩这些了哦,改行了。。。
感觉跟古老的telnet bbs有异曲同工之妙啊,都是直接读写文件的
如果只是把内存的数据存下来,为什么不用mmap呢?这样kernel会自动把内存的改动存在映射的文件中。
@dwing 试了下,可以的。 多谢
@lcinx mingw推荐用这个版本: http://www.tdragon.net/recentgcc/
请问下。你们用的mingw版本是? 最近换了mingw4.5.0。发现原有代码里的iocp部分不可用了。 用老版本的mingw3.4.5就好着。 纳闷了。
不喜欢回合制的游戏,感觉枯燥乏味
个人觉得把玩家息信全部序列化,然后存起来,这样做比之设计数据库表神马的更酷 因为属性序列化再存,可以使逻辑层程序员不用再关心数据库中存的是什么样的格式,从而降低逻辑层的开发成本 但是,由于息信被序列化了,所以底层开发人员需要再开发一个用于修改和查看序列化信息的文件的工具,加大了底层开发人员的工作量,但是长远来看,这样做是很值得的
祖国有你这样的人才,我感到很欣慰。
有闲置CPU时间,应该用来加大单服人数,而不是用来减轻磁盘IO.
原来,这样也可以
@Anonymous 是的,我的问题是对这篇文章前面一部分内容的询问,而非后面的IO优化。至于您所说的差异,我是以询问的态度希望以次获得对我的见解的肯定,或否定,以及正解,如果您能给出正解帮我解惑,不胜荣幸:)
@vincent 云风这篇文章讲的是对IO写入磁盘的优化。你的需求是怎么从IO中取得最快。如果你觉得差异在“从局域网获取数据比同机器共享内存中慢”,那确实没什么好讨论的。
优化确实蛮有意思的。 看得出,经验十分重要。
@lin_style 差异还是有的,直接对内存操作,总好做IO,不过我当初就是担心core什么的,不过开块共享内存想必会好很多。不过如果单指最后做IO,是直接写文件还是数据库这个我就不太清楚优劣了
@vincent 没差异。这东西做着做着就变成一个数据库的解决方案了,而且还只是其中的一部分功能而已。
但是对于数据库的操作是异步的啊 本身并不会降低主逻辑的吞吐量 我一直认为,这个效率应该在一个程度上就行,而且我也一直认为对数据库做足够的优化就差不多了 可能是我没有去做过极限或者大规模的测试
文本..... 不用数据库明智, 为什么要用文本??
但是数据的可持久性肯定会受到影响吧,以前是多个snapshot,现在是一个snapshot跟着好多的diff,任何一个diff损坏了就没办法复原了。
@vincent 他这个方案更多的是解决了IO操作的负载。因为只读写“脏数据”到磁盘,IO数据量减少了很多,所以会带来很大的效率提升。
俺是新人 俺们现在的服务端逻辑有大量的操作是直接异步操作数据库 俺想问问这样的模式与您所说的 直接扔内存里,开个共享内存用来存储,这样的模式比较起来,性能是否有所差异?
前辈能否看看邮箱,有个coroutine channel方面的一个思路想请教一下。
那大话西游呢?虽然已经离开大话一年多了,不过仍然对“卡”的问题印象深刻

Post a comment

非这个主题相关的留言请到:留言本