分享
 
 
 

案例分享:梦幻西游服务器的优化-服务器

王朝数码·作者佚名  2011-01-10
窄屏简体版  字體: |||超大  

在历史工程上修补是件麻烦的事情。

前两天说起梦幻西游服务器的优化。这几天我到广州住下来,打算专门花一周时间搞定这件事。由于以前都是网上聊天,只有坐到一起才能真正理解问题。

目前,梦幻西游,只使用单台机器,最高配置 8 个 CPU ,配置 8G 内存。就算最热闹的服务器,也用不完这些资源(大约只用满了 3 个 CPU ,一半的内存)。核心程序差不多就是 10 年前写的,从大话西游延续至今。这两年一直在享受免费的午餐,随着硬件配置提升,现在单台服务器同时在线容量达到一万两千人。观察服务器回应速度的图表可以发现,目前的问题在于,定期会出现反应迟钝的现象。周期性的,服务器回应时间会超过 1000ms 。查得原因在于那个时候,磁盘 IO 非常拥塞。有定期保存玩家数据的服务对 IO 的占用,以及 SA 做的定期备份数据的脚本占用了大量的 IO 时间。最终造成了机器负荷过重。

IO 负荷过重最终怎样影响到游戏服务的性能,这个暂时不过于深入探讨。我这两天主要是分析以有的系统结构,并想一下改进方案。

其实老的系统并不复杂,代码量也相当之小。相关的服务代码仅仅数千行干净的 C 代码而已。一直没有人动它,因为事关重大,牵扯着数百万用户的数据,以及记费流程。无论设计是好是坏,实现的性能有无问题,都让位于稳定。“历史原因”造成的种种,也只能在闲聊时抱怨一句,如果重新设计,肯定不会这样写了。近两年,我越发的对重构这件事情显的兴趣漠然,为何不这样做,为何不那样? 更多的时候都只是程序员们饭局上的聊资。每个系统一旦编写完成,就充满了种种的遗憾。如果它能用,最大的可能就是它就将一直用下去。一切的新想法,留给下一次吧。

对于已经稳定运行了很多年的陈旧的系统,找到好的方法去改造的意义不大。最重要的是,如何对已有系统影响最小的增加一些东西,提高性能。模块间清晰的划分显得相当重要。服务的独立性也是必要的。现在运行的数据服务和记费以及用户鉴权服务居然放在一个服务程序中恐怕是一个大失误。它使得我们把数据读写剥离出来非常困难。

数据服务采用的是一个 C/S 结构。但没有使用数据库,而是直接使用的本地文件系统。整个设计算是良好,但数据服务本身的机制却很糟糕。C 和 S 之间采用共享内存交换数据,这是为了提高 IPC 性能。C 只有一个,就是游戏主进程,而 S 可以有多个。可以并发的提供服务。多个 S 和 C 之间用管道传输命令,用共享内存交换数据。本意是好的,但协议设计是有问题的。因为 C 直接操控数据区,而有唯一性,结果设计时,把数据区的区块管理放在了 C 上,而不是由 S 提供。

举例来说,如果游戏进程(C) 需要加载一个用户的数据,它自己先寻找数据区中的空位,然后通知 S 把这个用户的数据加载到它指定的数据位置。数据区的清理工作同样是由 C 这边做的。这使得 S 不能直接在数据区上做 Cache ,如果需要 Cache 暂时不用的数据(比如一个玩家离线)就得由 C 自己来做。或者额外的再做一个 Cache 服务(这需要多出一倍的内存,以及内存复制的操作)当初这么实现恐怕是考虑到有多个 S 同时为一个 C 服务的需求,但我只能认为是设计欠佳。

结果就是,整个数据服务,无论是读还是写,都是无 Cache 的。Cache 仅仅依赖 OS 来做。对于当初低一个数量级的时候,这没有问题。但在线人数从千级达到万级后,问题就显露出来了。毕竟你为最终需求最更多的定制,越能充分发挥硬件的性能。

下面记录一下我已经实现好的内存 Key/Value 数据库的设计思路。

要实现前几天想好的,只保存差异信息的策略(经实测,可以减少 90% 的写 IO 操作),必须先统一数据读写服务的位置。不能依赖本地文件系统做数据交换。我之前考察过若干内存数据库,比如 Redis ,最终决定自己实现一个。因为我已经非常了解需求,可以高度定制算法,最大发挥硬件的能力。代码量也不会太大。(控制在 500 行 C 代码之内,最后实际写下来,不过 300 行 C 程序)

我们的需求是这样的:服务程序每周会停机一次。每周总共涉及的玩家数据 10 万组。每组数据 4k 到 32K 之间。都是文本数据。可以看成一个 id 到数据串的 key/value 数据储存服务。经估算,总数据可以全部放入内存。数据会频繁更新,更新后长度会改变。

我花了一天实现这个 k/v 内存数据服务。为了最大利用内存,并同时保证效率,以及代码实现的简洁。我采用预先分配好整块内存的方案,把内存切割成 1K 为单位的区块。并用单向链表串起来。考虑到内存 cache 的命中效率。链表指针本身和数据储存区分离。(大多数时候,我们只需要访问链表指针,而不需要访问具体数据)

链表指针采用序号,而非内存地址。这样即使在 64bit 系统上,依然使用 4 字节索引(可以最大可管理 4T 数据,足够了)。单向链表可以比双向链表节省一半的指针操作以及节约少量内存。代价是代码写起来繁杂一点。

所有内存区块分成两部分:空闲区块和已用区块。一开始全部空间都是空闲。一旦向内放入一段数据,就从空闲链表上摘下够用的区块,放到已用链表的尾部。如果 cache 空间满,则从已用区块链表头部移掉一些空间还给空间区块(这些数据区是长久未访问过的)。每次读取一段数据,都将其调整到已用链表的末尾,保证最后才清理。

另外做一个 hash 表,从 id 映射到在 cache 中区块段的头(由于是单向链表,具体实现时应保存上一个节点)。这样可以用 O(1) 时间查询指定 id 对应的数据区,

保存在 cache 中的数据不必在地址上完全连续,这好比磁盘的分簇管理。和磁盘不同,内存的随机访问性能和顺序访问性能差异更小。这样有利于内存空间利用效率。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有