最近主要忙着功能开发,一时感受没什么值得分享的。索性就数据存储这个话题聊聊,顺便本身也理一下思路,但愿后面作起来能顺利一点,目前咱们项目数据存储还彻底没作,只是简单的把每一个玩家数据单独存一个文件在硬盘上:-)html
网络游戏的数据存储有其特殊性。游戏是交互性比较强的产品,对系统响应时间的要求特别高,若是在处理逻辑时同步地去进行数据库IO是不可接受的。另外玩家在游戏过程当中数据变化很是剧烈,也就是说写数据的频率会特别高,若是设计不合理,数据存储是很容易成为系统瓶颈的。linux
不妨拿BBS系统来作一个比较。咱们逛BBS时大部分时间都是在浏览,相比之下发帖行为是小几率事件。用户花20分钟写完一个帖子点击发布按钮,3秒钟或10秒钟后提示发送成功都是彻底能够接受的,甚至提示发送失败要求用户从新再发一次也不是什么大问题。因此对于上述BBS系统,在用户提交请求时同步去读写数据库是彻底可行的。而网络游戏要作的是同时支持数千人以数秒为间隔不停地发贴,要保证响应时间控制在数毫秒内,还要保证发出去的帖其余人能当即看到。程序员
业内的通行作法是将数据库的职能尽可能简化,只拿数据库作数据最终的备份仓库来用。即在服务器进程启动时从数据库中加载全部的全局数据,在玩家上线时从数据库中加载玩家的全部数据,以后全部的逻辑操做都在内存中进行,从而避免游戏过程当中读写数据库带来的响应过慢。变化的数据以必定的时间间隔(通常为数分钟)异步写入数据库,这样就缓解了数据库写的压力。数据库
很显然,这样一来数据库所须要的功能是如此简单,只须要有GET和SET两个接口就彻底够用。因此选择合适的NoSQL数据库来提升数据存取性能也就理所固然了,必要的时候加入memcache来提升读数据的速度也是瓜熟蒂落的事情了。这也是咱们如今彻底没有数据库只是存文件也能正常开发功能的缘由——说到底读文件和写文件也就是GET和SET操做嘛,到时候换一下接口就好了。缓存
固然了这套作法是从传统的MMORPG演化而来的。如今大量的手游交互性比较弱,实时性不强,用户量小时处理逻辑时直接去读写数据库每每也彻底过得去。可是在系统设计阶段就把数据存储的隐患避免掉仍是有好处的,万一哪天游戏火了呢?云风的博客上有具体的案例能够参考:谈谈陌陌争霸在数据库方面踩过的坑(排行榜篇)。服务器
丢档多是游戏服务器程序员永远的噩梦,虽然没亲身经历过,我相信不幸遭遇运营中游戏丢档并处理过数据恢复的程序员心中应该都有不可磨灭的创伤……网络
就像策划每每不理解为何写一个100%不会崩溃的程序那么难,软件程序员每每也不理解为何服务器主机会宕掉,硬盘会直接被写废。实际上在硬件工程师看来,电脑硬件不断出各类问题才是正常的吧。嗯我想说的是,咱们应该在系统设计时就应该考虑好容灾,尽可能下降系统故障带来的损失。框架
根据前面的讨论,为了快速响应客户端请求,也为了下降数据库写的压力,变化的数据并无当即写进数据库,而是以必定的时间间隔存盘。这个取巧的作法其实有很大的问题:咱们修改完内存中的数据后就告知客户端操做完成,但这时数据并无成功落地,若是这时游戏进程异常崩溃就会形成回档,回档的最大时长为存盘间隔(数分钟)。运维
那么游戏进程能不能不崩溃呢?有可能,可是比较难,由于游戏进程每每复杂而且迭代很疯狂,要100%保证不崩我以为还得看具体的语言和框架。仍是那句话,咱们尽可能在设计阶段就把风险规避掉。异步
主要思路就是用一个稳定的进程来分担风险。前面提到能够加入memcache进程提升读数据的性能,很天然地咱们能够利用memcache这个稳定的进程来暂存数据。方案是这样的:每台有游戏进程运行的主机上都启一个memcache进程并一直运行,读数据时先从memcache读,若读取失败再到数据库读取,当玩家数据变化时同步写入memcache。这样即便游戏进程崩溃,重启后会首先从memcache中读出正确的数据。
往memcache写数据通常是经过HTTP或socket,会对客户端请求响应速度略有影响,因为两进程在同一主机,通常来讲是能够接受的。若是游戏对响应速度特别敏感,能够用共享内存的方式进行进程间通讯,前几年在畅游作MMORPG就是用的这种方式,只是共享内存并不易实现,程序复杂度比较高。这两种方式没有对错之分,只是须要根据游戏的需求权衡。
按照以前的设计,游戏进程和memcache都在同一主机,一旦主机崩溃不免会形成丢档。这依旧是一个须要权衡的问题。
游戏数据库硬盘每每使用RAID技术,理论上几乎不可能出现损坏。可是就怕碰上天灾人祸,机房天然灾害也好,程序员误操做也好,若是不作好备份一旦出了问题绝对是致命的,直接毁掉一家公司也不是没可能。
数据库备份从技术上其实没什么好说的,就是主从备份读写分离什么的。这里我想分享以前的一点经历。
在上家公司用的数据库是Tokyo Cabinet,这是一款很简洁的KV数据库。问题是貌似不多有人用,文档也比较匮乏,后来咱们在实际部署时不知道是bug仍是配置不对,主从数据库老是会有些不一样步。折腾了一段时间后咱们干脆换了思路,不依赖主从同步机制了,直接在存盘的时候分别往两个数据库存盘。由于存盘过程是异步的,系统的瓶颈并不在这儿,因此存一次仍是存两次也就无所谓了。若是对数据库的运维不熟悉而头疼的话能够考虑下这个思路:-)
游戏服务器领域其实比较封闭,上文涉及到的NoSQL、memcache都是从Web发展出来的组件,某种程度上来讲并非特别适合游戏服务器。上面的设计中游戏进程承担了太多数据存储的功能,好比对玩家数据的变化一方面要同步存入memcache,一方面又要掐表计时并按间隔存入数据库;再好比读数据时先要去memcache中查询,发现缓存失效时还要去数据库中读取。
我认为能够把数据存储部分彻底从游戏进程中抽离出来,只提供给游戏进程GET和SET接口,这样游戏进程就能够专一于处理逻辑了。抽离出来的进程也就是想象中的proxycache了,能够用来替代前面设计中的memcache。
proxycache是两种角色的结合体。
首先它做为proxy是游戏进程访问数据库的代理。收到GET请求时自已去向数据库请求后转发回游戏进程,收到SET请求时按照必定的间隔写入数据库。能够在SET参数中加入延时参数,好比不重要的数据变化能够接受5分钟内存盘,重要的数据变化能够要求当即写入数据库。proxycache内部根据请求参数排好优先队列后依次存储,这样能够将异常宕机带来的损失降至最低。
其次它做为cache能缓存数据。从数据库读回来的数据和游戏进程写进来的数据均可以缓存起来,不但实现了memcache的功能还简化了游戏进程的逻辑。
固然了如今都只是纸上谈兵而已,但愿以后抽个空给实现出来:-)
咱们项目数据库彻底没开始作的一个主要缘由是还没选好用什么数据库。目前我对数据库的指望是这样的:首先是足够简单的NoSQL数据库,最好是最简单的Key-Value,根据咱们的设计Key只须要string,Value只须要Blob就彻底够用了;而后但愿是作为独立进程运行;再就是运维要方便,若是数据文件只有一个那是极好的。
先是看了Redis。坦率地说,我认为Redis并不适合网络游戏。Redis原本是设计为内存数据库,在数据落地方面处理得比较粗糙,bgsave采用的fork方式,必须预留出足够的内存,怎么都以为浪费。并且网络游戏运营一段时间后每每数据库中大部分都是冷数据,Redis不分青红皂白一股脑读进内存实在是有些不可接受。可能仍是用做缓存比较对路吧。
再就是MongoDB。功能强大可是复杂度大大超出了咱们的指望,出了问题怕是难以驾驭。
如今主要是两个想法:
各位有了解合适的吗?求推荐!