程序员游戏开发

记一次游戏离线统计数据任务的测试和优化经历

2020-08-30  本文已影响0人  higher2017

概述:

本文总结了 —— 通过测试和优化几个很耗时的离线统计数据任务,从而避免了一场发版事故的经历。

出于各种原因(修正bug导致的错乱数据、活动更新需要清除旧数据等等),很多游戏项目经常需要在停服的时候进行一些离线任务对数据进行操作(这个操作在文中统一称为Job,一般操作的对象为数据库)。
作为开发人员除了要保证这些job的正确性,还必须考虑其内存、耗时等各方面的情况。比如某些job涉及的数据量十分巨大,那么Job中所有的要将数据加载到内存中的操作都要十分小心,否则有OOM的风险。并且数据量巨大的情况下,也必须要考虑执行job的耗时情况。否则因为job耗时太久导致延迟开服,甚至job还没跑完就要被迫kill掉,这一些列的问题都是十分严重的。
很多游戏的发版时间都是选择在深夜进行,这些Job出了问题很有可能相应的开发人员不在,问题会变得更复杂。所以提前对自己的Job进行内存和耗时测试是十分重要的(不止是正确性的测试)。

需求背景介绍:

最近我们的游戏有一个 “周年回忆” 的功能需要统计玩家一些历史数据(如:历史总收花数量、历史总送花数量、历史总登录天数等等),用于玩家回顾自己过去一年玩家在游戏中的点点滴滴(PS:和支付宝的年终总结差不多)。该功能有些有需要的数据并不能直接从玩家数据中获取,而要通过跑job离线统计的方式获取然后插入到指定的数据表中,然后玩家从这个表获取。比如上面说到的三个历史数据就是如此。(为什么不玩家登录的时候对自己的数据进行统计?因为这样服务器吃不消,所以要预先加载数据。)

Job介绍:

要跑job离线统计的数据一共有4个:

      1. 玩家历史登录天数;
      2. 玩家历史送花数量;
      3. 玩家历史收花数量;
      4. 玩家历史加好友数量.

这几个值数据库是没有的,但是可以通过Job遍历相关的表将玩家的这些数据统计出来。一开始的方案是分成4个job然后串行执行:

第一步(第一个Job):先统计玩家历史登录天数,然后将数据REPLACE到指定最终数据表A中(用于保存跑Job得到的数据);

第二步(第二个Job):统计玩家历史送花数,然后UPDATE到表A中;

第三步(第三个Job):统计玩家历史收花数,然后UPDATE到表A中;

第四步(第四个Job):统计玩家历史加好友数量,然后UPDATE到表A中。

每一步(每个Job)都是串行执行,必须等上一步(上一个Job)执行完才能执行下一步(下一个Job)。

这四个离线统计Job一共涉及4个全服玩家数据表,使用information_schema查询出线上某个用户分库的这四个表的数据量如下表。

表1

按照上面的数据量来看,对这些job进行耗时测试是十分有必要的。假如这些job不能控制在2个小时之内执行完毕就会影响发版,而且这个功能也会延期。

测试结果:

下表10W数据的Job耗时测试结果:


表2

那么按照表1的数据量,后面三个包含UPDATE操作的Job在单个用户分库的执行时间大概要2.5个小时(注意:上面的测试的表原始数据量是10W)。线上十几个分库,每个用户分库单表数据在100W级别。如果是以每个库并行执行的情况来看,后面三个Job(串行执行)要7.5个小时,这是绝对不能接受的。

原因分析:

从测试的结果很容易发现UPDATE的耗时明显比REPLACE高很多。明明代码中写的都是批量执行的,为什么差别如此大。这是因为UPDATE是批量发送到MySQL但是MySQL实际执行的时候还是一条一条的执行,每执行一条UPDATE语句就要加锁、binlog这些操作,实际表现上就只是减少了网络I/O,而并没有达到批量执行的效果。

批量发送UPDATE:

   dateBaseInstance.batchUpdate(UPDATE_SQL, updateDateList);

通过使用MySQL的 show profiles; 测试出执行单条UPDATE语句的耗时在0.005-0.02S 之间(结果如下图)。这个结果和上面的测试结果是吻合的。


image.png

再来看看REPALCE(INSERT也一样)的批量操作在MySQL中的执行逻辑。批量replace在数据库中的执行是真的批量执行,加锁和binlog都只是一条。所以耗时较少。

解决:

从上面的测试和分析结果,问题的主要原因就在UPDATE这上面,只要避免全服玩家数据UPDATE就行了。通过将4个统计job合并成一个,然后UPDATE都合并成一个REPLACE问题就解决了。通过测试内存和耗时都到达要求。

总结反思:

涉及全服玩家数据查询和更新的job一定要谨慎,像这样的job很大可能性会有内存或者耗时的风险。我们项目组解决内存风险的手段就是通过串行的方式一批一批(现在是10W作为一批)地对用户数据进行查询和操作。这种方式确实避免了OOM,但是也会照成耗时过大的风险。对需要全服玩家都UPDATE操作的一定要进行耗时测试。

上一篇下一篇

猜你喜欢

热点阅读