大家在玩鹅厂系游戏的时候应该看到过xxxRpcs.dll TCJ.dll ProbePolicy.dll 这样的dll,会上报一些游戏内和游戏外的数据。游戏内的数据比如游戏的某个函数被call了多少次,call的参数有没有被人为修改,调用来源是否合法...甚至键盘某个键按下了多少次(对比正常玩家和开挂玩家之间的数据差异来抓外挂)。游戏外的数据比如你开了哪些进程,开了哪些窗口,如果是不认识的进程是不是要hash一下把hash也上报服务器。这些数据大多都是“
时序数据
”。
时序数据有什么特点?百度上面的解释太多了,我用我自己的话描述一下:
1、写入(insert)量非常大、更删查(update delete select)很少甚至没有
2、数据之间没有关联性,用搞web的话说就是没有join
3、数据一般不会永久保留,XX天之后就会删除(也就是说有按日期批量删除的需求)
据不可靠消息来源,鹅厂TP的外挂数据存储是mysql(你信吗?我反正不信)
笔者四年前开发外挂数据存储时第一次用的就是mysql,写入性能捉鸡,上报用户数量一多,mysql的cpu使用率就暴涨,没一会儿mysql自己卡死,上层服务就全挂掉了
4核8G内存mysql顶多只能写入几百一千条/s,这还是开了TRANSACTION的情况下的,要是一条一条裸奔INSERT,那效率惨不忍睹....
而且当每天增长几GB数据、不得不面临加硬盘/清理旧数据的选择的时候
加硬盘?more money please
清理旧数据?delete from XXX where time < YYY 然后锁表慢慢跑去吧...没个七八个小时别想跑完
后来调研了一波当时市面上存在的写入性能比较好的数据库,相中了mongodb,但是也是撑死只能做到
16核32G 3000条/s,最高峰维持在40%CPU使用率(如果降低到8核的话业务最高峰99%使用率瞬间爆炸)。
随之而来的是每个月10K+ RMB的数据库费用...
而且更坑爹的是,mongodb在当前业务中完美继承了mysql的那些缺点:
删数据=锁表
锁表就算了,我整个表直接drop
偶尔的查字符串$regex:{xx:'yyy'} = 漫长的等待
我打完字符串查询语句,去泡了杯茶
回来一看,emm还在查询...下楼去买他一波饮料。
回来一看,emm还在查询...当我正要点Cancel把查询operation kill掉的时候。
查询结果出来了。本次共查询到了5个document(s),耗时10mins 35 secs
这mongodb还让不让人干活儿了。
咱不受这气!换!
一个月后我把之前的纯mongo存储换成了elasticsearch+mongo双存储。
一开始是上层服务直接往es bulk数据,一次几十~一百多条,一到高峰期就疯狂es reject,优化了index的refresh interval也没啥用。
考虑到es是通过HTTP client写入的,一秒几十个后端服务同时发起几百个bulk和get请求这http怕是吃不消。
后来我把写入逻辑改成了上层服务把所有数据写到logstash,logstash再往es写数据,一次写1500条,大幅度减少了同时发起的HTTP请求。
elasticsearch只存纯时序数据,按日期拆分index。
mongo只存可能需要中途更新或者频繁修改的数据,修改完成之后同步一份完整数据到es。
用3节点es成功做到了最高峰3000 index /s, 0 reject
考虑到用logstash首次迁移数据时最高是8000条/s,所以以目前的配置哪怕最高峰承载的人数翻一倍问题也不是很大。
总结:es大量写入数据务必通过logstash,避免大量零散get/mget,否则非常容易因为同时发起的请求过多导致reject 。
目前mongo和es最高峰的cpu使用率都在40%以下,而且数据库费用也降到了原来的五分之一。
按日期拆分index之后每天还能用es自带的生命周期管理功能自动清理旧的数据,最爽的是搜索字符串1秒内出结果。
芜湖~起飞
如果不考虑数据时效性(数据延迟几个小时)为了节约成本还能继续优化,把数据放到kafka之类的磁盘队列里面,然后在业务低峰期(比如深夜凌晨)写入,能够平衡高峰和低峰期的服务器负载。
[课程]FART 脱壳王!加量不加价!FART作者讲授!