陌陌争霸霸王的大陆大陆分服怎么回事

2144人阅读
NoSQL(10)
转自&/2014/03/mmzb_redis.html
注:陌陌争霸的数据库部分我没有参与具体设计,只是参与了一些讨论和提出一些意见。在出现问题的时候,也都是由肥龙、晓靖、Aply 同学判断研究解决的。所以我对 Redis 的判断大多也从他们的讨论中听来,加上自己的一些猜测,并没有去仔细阅读 Redis 文档和阅读 Redis 代码。虽然我们最终都解决了问题,但本文中说描述的技术细节还是很有可能与事实相悖,请阅读的同学自行甄别。
在陌陌争霸之前,我们并没有大规模使用过 Redis 。只是直觉上感觉 Redis 很适合我们的架构:我们这个游戏不依赖数据库帮我们处理任何数据,总的数据量虽然较大,但增长速度有限。由于单台服务机处理能力有限,而游戏又不能分服,玩家在任何时间地点登陆,都只会看到一个世界。所以我们需要有一个数据中心独立于游戏系统。而这个数据中心只负责数据中转和数据落地就可以了。Redis 看起来就是最佳选择,游戏系统对它只有按玩家 ID
索引出玩家的数据这一个需求。
我们将数据中心分为 32 个库,按玩家 ID 分开。不同的玩家之间数据是完全独立的。在设计时,我坚决反对了从一个单点访问数据中心的做法,坚持每个游戏服务器节点都要多每个数据仓库直接连接。因为在这里制造一个单点毫无必要。
根据我们事前对游戏数据量的估算,前期我们只需要把 32 个数据仓库部署到 4 台物理机上即可,每台机器上启动 8 个 Redis 进程。一开始我们使用 64G 内存的机器,后来增加到了 96G 内存。实测每个 Redis 服务会占到 4~5 G 内存,看起来是绰绰有余的。
由于我们仅仅是从文档上了解的 Redis 数据落地机制,不清楚会踏上什么坑,为了保险起见,还配备了 4 台物理机做为从机,对主机进行数据同步备份。
Redis 支持两种 BGSAVE 的策略,一种是快照方式,在发起落地指令时,fork 出一个进程把整个内存 dump 到硬盘上;另一种唤作 AOF 方式,把所有对数据库的写操作记录下来。我们的游戏不适合用 AOF 方式,因为我们的写入操作实在的太频繁了,且数据量巨大。
第一次事故出在 2 月 3 日,新年假期还没有过去。由于整个假期都相安无事,运维也相对懈怠。
中午的时候,有一台数据服务主机无法被游戏服务器访问到,影响了部分用户登陆。在线尝试修复连接无果,只好开始了长达 2 个小时的停机维护。
在维护期间,初步确定了问题。是由于上午一台从机的内存耗尽,导致了从机的数据库服务重启。在从机重新对主机连接,8 个 Redis 同时发送 SYNC 的冲击下,把主机击毁了。
这里存在两个问题,我们需要分别讨论:
问题一:从机的硬件配置和主机是相同的,为什么从机会先出现内存不足。
问题二:为何重新进行 SYNC 操作会导致主机过载。
问题一当时我们没有深究,因为我们没有估算准确过年期间用户增长的速度,而正确部署数据库。数据库的内存需求增加到了一个临界点,所以感觉内存不足的意外发生在主机还是从机都是很有可能的。从机先挂掉或许只是碰巧而已(现在反思恐怕不是这样, 冷备脚本很可能是罪魁祸首)。早期我们是定时轮流 BGSAVE 的,当数据量增长时,应该适当调大 BGSAVE 间隔,避免同一台物理机上的 redis 服务同时做 BGSAVE ,而导致 fork
多个进程需要消耗太多内存。由于过年期间都回家过年去了,这件事情也被忽略了。
问题二是因为我们对主从同步的机制了解不足:
仔细想想,如果你来实现同步会怎么做?由于达到同步状态需要一定的时间。同步最好不要干涉正常服务,那么保证同步的一致性用锁肯定是不好的。所以 Redis 在同步时也触发了 fork 来保证从机连上来发出 SYNC 后,能够顺利到达一个正确的同步点。当我们的从机重启后,8 个 slave redis 同时开启同步,等于瞬间在主机上 fork 出 8 个 redis 进程,这使得主机 redis 进程进入交换分区的概率大大提高了。
在这次事故后,我们取消了 slave 机。因为这使系统部署更复杂了,增加了许多不稳定因素,且未必提高了数据安全性。同时,我们改进了 bgsave 的机制,不再用定时器触发,而是由一个脚本去保证同一台物理机上的多个 redis 的 bgsave 可以轮流进行。另外,以前在从机上做冷备的机制也移到了主机上。好在我们可以用脚本控制冷备的时间,以及错开 BGSAVE 的 IO 高峰期。
第二次事故最出现在最近( 2 月 27 日)。
我们已经多次调整了 Redis 数据库的部署,保证数据服务器有足够的内存。但还是出了次事故。事故最终的发生还是因为内存不足而导致某个 Redis 进程使用了交换分区而处理能力大大下降。在大量数据拥入的情况下,发生了雪崩效应:晓靖在原来控制 BGSAVE 的脚本中加了行保底规则,如果 30 分钟没有收到 BGSAVE 指令,就强制执行一次保障数据最终可以落地(对这条规则我个人是有异议的)。结果数据服务器在对外部失去响应之后的半小时,多个
redis 服务同时进入 BGSAVE 状态,吃光了内存。
花了一天时间追查事故的元凶。我们发现是冷备机制惹的祸。我们会定期把 redis 数据库文件复制一份打包备份。而操作系统在拷贝文件时,似乎利用了大量的内存做文件 cache 而没有及时释放。这导致在一次 BGSAVE 发生的时候,系统内存使用量大大超过了我们原先预期的上限。
这次我们调整了操作系统的内核参数,关掉了 cache ,暂时解决了问题。
经过这次事故之后,我反思了数据落地策略。我觉得定期做 BGSAVE 似乎并不是好的方案。至少它是浪费的。因为每次 BGSAVE 都会把所有的数据存盘,而实际上,内存数据库中大量的数据是没有变更过的。一目前 10 到 20 分钟的保存周期,数据变更的只有这个时间段内上线的玩家以及他们攻击过的玩家(每 20 分钟大约发生 1 到 2 次攻击),这个数字远远少于全部玩家数量。
我希望可以只备份变更的数据,但又不希望用内建的 AOF 机制,因为 AOF 会不断追加同一份数据,导致硬盘空间太快增长。
我们也不希望给游戏服务和数据库服务之间增加一个中间层,这白白牺牲了读性能,而读性能是整个系统中至关重要的。仅仅对写指令做转发也是不可靠的。因为失去和读指令的时序,有可能使数据版本错乱。
如果在游戏服务器要写数据时同时向 Redis 和另一个数据落地服务同时各发一份数据怎样?首先,我们需要增加版本机制,保证能识别出不同位置收到的写操作的先后(我记得在狂刃中,就发生过数据版本错乱的 Bug );其次,这会使游戏服务器和数据服务器间的写带宽加倍。
最后我想了一个简单的方法:在数据服务器的物理机上启动一个监护服务。当游戏服务器向数据服务推送数据并确认成功后,再把这组数据的 ID 同时发送给这个监护服务。它再从 Redis 中把数据读回来,并保存在本地。
因为这个监护服务和 Redis 1 比 1 配置在同一台机器上,而硬盘写速度是大于网络带宽的,它一定不会过载。至于 Redis ,就成了一个纯粹的内存数据库,不再运行 BGSAVE 。
这个监护进程同时也做数据落地。对于数据落地,我选择的是&&,几行代码就可以做好它的 Lua 封装。它的数据库文件只有一个,更方便做冷备。当然&&也是个不错的选择,如果它是用
C 而不是 C++ 实现的话,我会考虑后者的。
和游戏服务器的对接,我在数据库机器上启动了一个独立的 skynet 进程,监听同步 ID 的请求。因为它只需要处理很简单几个 Redis 操作,我特地手写了 Redis 指令。最终这个服务&&,其实它是由三个
skynet 服务构成的,一个监听外部端口,一个处理连接上的 Redis 同步指令,一个单点写入数据到 unqlite 。
晓靖不喜欢我依赖 skynet 的实现。他一开始想用 python 实现一个同样的东西,后来他又对 Go 语言产生了兴趣,想借这个需求玩一下 Go 语言。所以到今天,我们还没有把这套新机制部署到生产环境。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:703779次
积分:7543
积分:7543
排名:第1647名
原创:124篇
转载:118篇
评论:72条
(1)(2)(3)(1)(2)(5)(4)(6)(6)(2)(4)(5)(3)(10)(8)(7)(6)(6)(13)(4)(2)(12)(5)(2)(1)(3)(2)(3)(2)(1)(1)(3)(1)(2)(5)(5)(1)(18)(16)(2)(3)(5)(4)(3)(1)(8)(23)(9)(1)陌陌争霸里购买限量魔能礼包?是怎么回事_百度知道
陌陌争霸里购买限量魔能礼包?是怎么回事
提问者采纳
从20号到26号期间可以购买,里面包含2万酒和5000魔能是这样的花500宝石可以购买魔酒礼包,限购买10份
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁陌陌争霸怎么看所在区/服_百度知道
陌陌争霸怎么看所在区/服
我有更好的答案
只有正式服和尝鲜服
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁

我要回帖

更多关于 陌陌争霸 的文章

 

随机推荐