关注梦幻西游服务器的性能问题,是源于前几天跟同事的聊天。谈到能否把梦幻西游服务器做成无盘站,或是放进虚拟机里,便于日常维护管理。
意外的了解到,现在磁盘 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 量(如果加上压缩,将更客观),优化后的效果将非常明显。