知名网游公司的NoSQL数据库实践

日期: 2014-03-05 作者:吴云洋 来源:TechTarget中国 英文

编者按:本文节选自简悦的创始人云风的博客,其中作者深入探讨了公司的网游产品陌陌争霸在NoSQL数据库应用方面所遭遇的一些问题,以及应对的方法。对有类似应用场景的用户,本文极具参考价值。更多的细节内容,请访问云风的Blog

陌陌争霸在服务器方面的选型和构架按着这个思路做出来:

  • 我们用 redis 保存玩家的数据,考虑到玩家数量可能很多,一个 redis 仓库可能不够,我们使用了 32 个 redis 仓库,按玩家 id 分开存放。
  • 而运营 log 和一些随时间自然增长的数据,比如战斗录像,我们选择了不受内存限制,且易于做数据分析的 mongodb 。由于担心数据量过大,使用了 mongos 分片

Mongo数据库

我们公司开始用 mongodb 并不是因为开始的技术选型,而是我们代理的第一款游戏《 狂刃 》的开发商选择了它。共同开发期间,我们搭建的运营平台自然也选择了 mongodb 作为数据库,这样维护人员就可以专心一种数据库了。

陌陌争霸出的第一起服务器事故发生在今年1月12日。我们发现运营数据要延迟几个小时才能到达运营平台。

这表面上看起来是在 mongos 服务上堆积了大量的数据库插入操作。让这个单点过载了。我们裁减并精简了一部分 log 但似乎并不能从根本上解释这起事故。

但其实问题出在 mongos 的 shard key 的选择上。mongo 可以指定 document 的若干字段为 shard key ,mongos 把这个 key 当成一个整数,按整数区间把 document 分成若干个桶。再把桶均匀分配到背后的从机上。

如果你的 key 是有规律的数字,而你又需要这种规律不至于破坏桶分配的公平性,你还可以将一个 hash 算法应用于原始选择的 key 上,让 key 足够散列开。我们一开始就是按自增 id 的散列结果做 key 的。

错误的 shard key 选择就是这起事故的罪魁祸首。

因为我们用大量的顺序写操作,应该优先保证写入的流畅。如果用随机散列的方式去看待这些 document 的话,新旧 log 就很大几率被分配到一起。而 mongo 并不是一条一个单位将数据落地的,而是一块块的进行。这种冷热数据的交织会导致写盘 IO 量远远大于 log 实际的输出量。

最后我们调整了 shard key ,按 log 时间和自增 id 分开,就把 mongo 数据落地的 IO 量下降了几个数量级。

看吧,理解系统如何工作的很重要。

Redis

在陌陌争霸之前,我们并没有大规模使用过 Redis 。只是直觉上感觉 Redis 很适合我们的架构。

我们将数据中心分为 32 个库,按玩家 ID 分开。不同的玩家之间数据是完全独立的。前期我们只需要把 32 个数据仓库部署到 4 台物理机上即可,每台机器上启动 8 个 Redis 进程。一开始我们使用 64G 内存的机器,后来增加到了 96G 内存。实测每个 Redis 服务会占到 4~5 G 内存,看起来是绰绰有余的。为了保险起见,还配备了 4 台物理机做为从机,对主机进行数据同步备份。

Redis 支持两种 BGSAVE 的策略,一种是快照方式,在发起落地指令时,fork 出一个进程把整个内存 dump 到硬盘上;另一种唤作 AOF 方式,把所有对数据库的写操作记录下来。我们的游戏不适合用 AOF 方式,因为我们的写入操作实在的太频繁了,且数据量巨大。

这次事故出在 2 月 3 日。

中午的时候,有一台数据服务主机无法被游戏服务器访问到,影响了部分用户登陆。在线尝试修复连接无果,只好开始了长达 2 个小时的停机维护。

在维护期间,初步确定了问题。是由于上午一台从机的内存耗尽,导致了从机的数据库服务重启。在从机重新对主机连接,8 个 Redis 同时发送 SYNC 的冲击下,把主机击毁了。

为何重新进行 SYNC 操作会导致主机过载?

主要是因为我们对主从同步的机制了解不足

由于达到同步状态需要一定的时间。同步最好不要干涉正常服务,那么保证同步的一致性用法肯定是不好的。所以 Redis 在同步时也触发了 fork 来保证从机连上来发出 SYNC 后,能够顺利到达一个正确的同步点。当我们的从机重启后,8 个 slave redis 同时开启同步,等于瞬间在主机上 fork 出 8 个 redis 进程,这使得主机 redis 进程进入交换分区的概率大大提高了。

在这次事故后,我们取消了 slave 机。因为这使系统部署更复杂了,增加了许多不稳定因素,且未必提高了数据安全性。同时,我们改进了 bgsave 的机制,不再用定时器触发,而是由一个脚本去保证同一台物理机上的多个 redis 的 bgsave 可以轮流进行。另外,以前在从机上做冷备的机制也移到了主机上。好在我们可以用脚本控制冷备的时间,以及错开 BGSAVE 的 IO 高峰期。

第三次事故出现在2 月 27 日。

我的同事在原来控制 BGSAVE 的脚本中加了行保底规则,如果 30 分钟没有收到 BGSAVE 指令,就强制执行一次保障数据最终可以落地。结果数据服务器在对外部失去响应之后的半小时,多个 redis 服务同时进入 BGSAVE 状态,吃光了内存。

花了一天时间追查事故的元凶。我们发现是冷备机制惹的祸。我们会定期把 redis 数据库文件复制一份打包备份。而操作系统在拷贝文件时,似乎利用了大量的内存做文件 cache 而没有及时释放。这导致在一次 BGSAVE 发生的时候,系统内存使用量大大超过了我们原先预期的上限。

这次我们调整了操作系统的内核参数,关掉了 cache ,暂时解决了问题。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

电子邮件地址不会被公开。 必填项已用*标注

敬请读者发表评论,本站保留删除与本文无关和不雅评论的权力。

相关推荐