游戏数据的监控和挖掘:捕回游戏中的漏网之鱼

这是一篇只谈渔网和捕鱼的文章,换言之是只深入探讨各种捕获游戏中产生的流失数据的方法;
不再谈为什么了。在上一次里已经对数据的重要性以及数据积累、挖掘和分析的重要性谈了很长一篇文章了,这次把笔墨留给最有用的地方;
除此之外,我们也不会深入谈到以下内容:
捕鱼的手头技巧活——也就是各种捕捉技术的技术实现细节;
捕上来的鱼的挑选——换言之是数据的监控和挖掘;
鱼的烹饪——也就是数据的分析;
接下来,在正式探讨我们的渔网结构前,我想先用自己平常做的一件事做个类比,看看当前行业里很多公司的现状。

文/天之虹

一篇只谈捕鱼的文章

去年发了一篇关于数据挖掘和数据分析的文章《浅谈游戏数据的积累、挖掘与分析》,无论是从点击量还是从朋友口里的评论了解,这篇文章的反响都很不错。但这篇文章却留给了我几个遗憾:

上稿的缺憾。

犹记得在我07年刚入九城不久时进行了两次分享交流的讲座,讲座的主题正是数据挖掘和数据分析。年底PR部门希望籍着投稿来扩大研发中心的影响力,于是我把两次的讲座整理成文章给PR部门投稿到“游戏创造”。但过囧的是PR的人员给出的是我的英文名而不是中文名,于是无论是出来的杂志还是后来转帖的文章都印上了不为人所知的“Jackie chueng”,而我当时不求稿费只求增刊的心愿也一直没得到满足。

正是这个原因,使得我从当时开始就不再打算投稿到“游戏创造”了。

入门式的泛泛而谈。

上一篇文章是以讲座形式展开的,参与讲座的人在各块领域里都有,使得讲座无法深入地谈,只能成为一次泛泛而谈的入门介绍。当然也因为我写稿子的时候懒,只是把两个PPT还原成文稿,没加入任何新的内容。

只谈原因,不深入方法,这是我觉得上一篇文章最缺失的内容。

问题的悬而未决。

当时在公开讲座前,关于数据挖掘和数据分析的经验多来自06年在天晴的所见所学(还有一些国外的文章),但当时的天晴只属探讨,而当年的自己也经验不足,仅限旁听而没有实际参与。

做公开讲座时在这方面带着的问题很多,所以最终也只把它当做一次探讨。如今实际深入这方面的工作了,希望能突破前一篇文章的限制,谈谈我的一些构思。

———————————————————————————–

完全捕鱼手册

在这篇文章里谈及的内容有着以下特色:

  • 这是一篇只谈渔网和捕鱼的文章,换言之是只深入探讨各种捕获游戏中产生的流失数据的方法;
  • 不再谈为什么了。在上一次里已经对数据的重要性以及数据积累、挖掘和分析的重要性谈了很长一篇文章了,这次把笔墨留给最有用的地方;
  • 除此之外,我们也不会深入谈到以下内容:
    • 捕鱼的手头技巧活——也就是各种捕捉技术的技术实现细节;
    • 捕上来的鱼的挑选——换言之是数据的监控和挖掘;
    • 鱼的烹饪——也就是数据的分析;

接下来,在正式探讨我们的渔网结构前,我想先用自己平常做的一件事做个类比,看看当前行业里很多公司的现状。

Verycd的宝贵资源

记得我还在做广告时(04年)就养成了一个很特别的习惯:每天翻Verycd和BT大站,把当天看到的有用资源挂机下载下来,再刻录成DVD存放,至今已经下载和存放了几T的资源了。这些资源包括视频、素材(声音、模型、特效、动作等)、教程、文档、图片、引擎、书籍等资料。

在这5年的资源下载和收集里,我慢慢养成一种强迫症,觉得这些资源下载、收藏和归类下来是很有用的——即使眼下没用将来也会用到。的确,事实印证了我每一次的收集,这些资料都每每派上大用场了。

很多人平日没有这种习惯,Verycd上流转着的各种宝贵资源对他们只是流水般地经过,真到需要资料时才匆忙寻找,或抱怨一句“没有资料”。

所幸,电驴和BT的存在是让人们都很幸福的——在需要资料时即使间隔太长时间,一般还有驴友或者种子能下载到。

但游戏里的数据呢?

数据,大量流失的数据

游戏运营过程中每天产生的数据也像Verycd的首页那样不断更新,更新频率比它还要快上几千几万倍。不少人都会聪明地把这些资源“下载”到本地分类整理,事后靠这些资源得到很大益处。但那些不经意错过了这些资源的人,这辈子也无法再通过搜索引擎把它们找回来了——数据只在一刹间产生,而后瞬间消逝,犹如灵活的鱼群那样,看得到它们的穿梭,但不设渔网就会让它们永远地从你指间溜走。

面对这种危急的情况,我们是不是该赶快做点什么呢?

捕回你的漏网之鱼

知己知彼,在匆忙去定做渔网之前我们首先要先来看看这片海域里有哪些鱼,然后再针对这些鱼来做出最合适的渔网——游戏在运营过程中到底会产生哪些瞬时诞生而又瞬间消逝的数据呢?这总括下来有两大类,四小类:

  • 玩家行为

——换言之是由玩家行为产生的即时数据

例如玩家在NPC商店里购买物品,该行为涉及到玩家与NPC交互、玩家金钱的变动、玩家购买的物品清单记录,以及玩家背包里的物品变动。虽然这里有不少数据是重合的,但它们都是玩家在商店里购买物品这一行为引发的瞬时数据。

这种数据又分为两类:

  • 服务器判定到的行为

由于考虑到服务器压力和执行效率,玩家实际操作中有很多行为都是不会发送给服务器的。经过客户端发送前的过滤后,服务器真正接收到的玩家行为才会进行进一步的处理——这些接收到的行为就是服务器判定到的行为了。

或许光这么说还没什么概念,我们再来看下一类行为就明白了。

  • 客户端真正执行的行为

如前所说,有很多行为是虽然在客户端前发生了,但考虑到服务器效率和压力,是不会发送给服务器的——例如玩家操作。

在MMORPG里不熟练的玩家常常会乱敲技能键,尤其是《名将三国》一类考究操作的动作类横版游戏。但服务器处理消息的吞吐量是有限的,技能也有CT(施法时间)和CD(冷却时间)之分——所以在技能CT和CD过程中敲下的按键操作多数是被客户端忽略或屏蔽的。

在这种情况下,客户端真正执行的行为集是和服务器判定到的行为集有着很大差异的,而且这种差异基本是前者包含后者。

服务器判定到的行为一般在服务器处理后转为两种结果:

  1. 对玩家即时行为的反馈。

例如当玩家点击一个NPC后,服务器判定交互成功,通知客户端执行NPC的交互表现或者弹出对话框。

  1. 对该玩家或者其他玩家的数据修改。

修改的数据可能包括临时数据或存盘数据,例如玩家买卖物品后的背包物品和金钱变动,例如玩家PvP时双方的血蓝变化和Buff、Debuff变化情况等。

无论是哪种结果,在反馈完成或数据修改后,该项行为的所有信息都是转瞬即逝的。

至于客户端真正执行的行为就更是如此了,连服务器都不知道,估计知道的就只有屏幕前的玩家了——但玩家操作过就过了,谁会记得一场紧张的PvP里对某个技能敲了多少次按键呢?

  • 瞬时状态

——换言之是玩家或服务器瞬间的状态。

这些状态又分为两类:

  • DB(数据库)历史数据

DB(或者说数据存盘)在游戏运营过程中会非常频繁地更新。一旦玩家行为产生数据变动后,为了保证这些数据在所有情况下同步,服务器会把这些数据马上在DB里更新存盘。

但玩家在游戏里都是频繁操作的,例如背包的操作。

在短短1小时内玩家可能会打怪捡包、买卖物品、交易装备、搜刮一下拍卖行和摊位、再丢掉一些无用的垃圾,因为任务而得到物品或交还物品……如此下来在1小时内背包的历史数据会快速地变化更新。

这种快速更新导致DB的历史数据就像列车车窗的景色那样,在快速前行中不断变换,永远都留不住身后已过去的风景。

  • 服务器瞬时状态

服务器瞬时的状态就更像是自然人文风景那样了,你永远不能让马路上的车流人流永远静止在那样不动——景色依旧在演变。

例如服务器在某一时刻各地图的人数,这只是一个瞬间的值,或者是一段有限时间内的平均值,这个值是无法永远保留的。稍不留神就会瞬间闪过了。

面对这些灵敏穿梭在游戏这片海域里的鱼群,我们该定做什么样的渔网来无一遗漏地捕获它们呢?

这四类“鱼”无论是特性上还是行为特征上都有着很大差别,为了更有效且更有针对性地捕获它们,我们也要相应地分别定做5种不同的渔网,运用5类不同的捕鱼手段:

  • 针对服务器判定到的行为,我们要用Log或者是累加器的方式去捕捉;
  • 针对客户端真正执行的行为,我们要用客户端Trace的方式去捕捉;
  • 针对DB历史数据,我们要用DB快照的方式捕捉;
  • 针对服务器瞬时状态,我们要用定点主动统计的方式捕捉;

以上我们只是简单提及了5种不同的渔网和捕鱼手段,接下来我们逐一详细地看看吧。

µ    玩家行为的捕获                                

最完美的情况

我们来构思一种最完美的情况:

  • 假如客户端把玩家执行的所有行为都发送给服务器;
  • 假如服务器也强大到能把每一个行为都拍录下来;
  • 假如服务器上的存放空间无限大;

那我们只需要服务器做现场拍录的工作,把该行为牵涉的一切数据都无一遗漏地拍下来就好了。

换言之,我们能用最详尽的log方式——或者这种不叫log,而是把客户端发来的所有信息,以及服务器对这些信息的处理过程和结果都忠实地记录下来。

当然,最完美的永远是乌托邦。事实呢?

现实情况

现实情况是以上的“假如”我们一点都达成不了:

  • 客户端上有很多行为都不会发送给服务器

就以前面的技能CT和CD时间输入的操作来说,这些操作即使发给服务器,服务器也是处理为技能施放失败的。与其这样不如客户端把它先判为失败,过滤了这些信息,不发给服务器了。

那为什么要客户端先处理一道呢?因为……

  • 服务器的处理能力是有限的

服务器始终是一台或一组物理机器,就算如何强大,机器的处理能力都是有限的。加之瓶颈不在于机器处理能力,而在于带宽。这宝贵的带宽(在中国北网通南电信的环境里变得更加窘迫)当然只容下最宝贵的数据通过了——所以客户端能尽量过滤无效信息都会尽量地隔挡后再转发。

其次,服务器更主要的精力在于判定、处理和存储数据,光是这部分的压力已经让服务器疲惫不堪,让设计师们还贪得无厌了。如果说要从服务器的处理能力上再挖一刀,分给工作量庞大的数据记录的岗位时,服务器肯定会撒手罢工的。

  • 存储空间也是有限的

去年我配机时也轻轻松松地配了一个1T的硬盘,现在的存储空间已经大得很可观了,但再可观也是有限的存储空间——有限的存储空间总是会放满的。那肆无忌惮地往家里堆杂物,堆满以后等到真正有价值的珍藏品却放不进来,那该怎么办呢?

所以还是得有策略和有挑选地捕获。

眼前的现实情况让我们不得不放弃最完美的方案所带来的最轻松的方法,转而运用更合理的方式:

因时因地制宜地定制渔网

更为合理的方式是根据玩家的行为进行进一步细分,根据每一类的细分制定最合适的捕获策略,尽量用最有效且最节省服务器性能和空间的方式去制定渔网。

为了做到这点,我们先来对玩家行为进行进一步的细分。

玩家行为细分

就玩家行为来看,我们前面已经把这些行为分成两类了:

  • 服务器判定到的行为
  • 客户端真正执行的行为

我们先保证这两种行为是没交集的,换言之两者原本的交集部分会从后者的集合里剔除掉,也就是:

  • 服务器判定到的行为
  • 客户端真正执行的行为

——仅包括那些客户端执行了却没有发送给服务器的行为。

我们可以根据两个维度去对游戏中的各种行为进行划分:

  • 单个玩家该行为发生的频繁度:
    • 频繁:例如每个在线玩家一分钟里发生数十次的,这些理应算作很频繁的行为;
    • 稀少:例如玩家在整个游戏数百小时的生命周期里只发生100次不到的行为,理应是很稀少的;
    • 一般:频繁度介于这两者之间的行为;
  • 该行为在游戏价值观评判下的重要度:
    • 重要:牵涉着玩家的重大利益或者游戏里的重大价值的;
    • 不重要:即使行为回退也不会造成玩家或游戏明显损失的;
    • 一般:重要度介于这两者之间的行为;

按这种划分方法,游戏里所有的玩家行为都可以落入以下的九宫格范围里:

例如下图,下表中各类行为都可以归入这个九宫格的相应位置里(位置只是粗略放置用于说明的,不必考究准确位置)

行为 频繁度 重要度
打怪 玩家与怪物战斗过程中发生的行为 极为频繁 不太重要
杀死怪物 玩家在战斗后杀死了怪物的行为 比较频繁 有点重要
玩家间交易 玩家间交易物品或金钱的行为 一般 很重要
商城买卖 玩家在游戏商城里购买物品的行为 不太频繁 极为重要
玩家升级 玩家在游戏里提升等级的行为 极不频繁 很重要

 

Ok,基于这种细分,接下来我们分别来看看该如何因时因地制宜地定制三种不同的渔网:

Log机制

什么是Log机制?

Log是指日志的意思,也就是程序在运行过程中把运行的步骤和碰到的情况都记录下来,以便日后进行参考或调试。

换到游戏里,游戏中的Log指把玩家行为中牵涉的多项数据记录下来。

在前面的九宫格里,Log解决的主要是以下领域:

Log的两种格式

常见的log有两种不同的格式(这只是我自己定的便于分类的叫法):

  1. 文本式记录
  2. 表格式记录

以玩家攻击怪物这个行为为例,文本式记录可能会Log下类似如下的记录:

2010-2-5 10:35:23 玩家[PlayerID 65332548]用技能[SkillID 521665]攻击怪物[MonsterID 230005],成功命中[HitType 1],造成伤害[DamagePt 648]点。

而表格式记录会在玩家攻击怪物表(Player2Monster)里Log下类似如下的数据:

PlayerID SkillID MonsterID HitType DamagePt
65332548 521665 230005 1 648

无论是以上的哪种方式,Log记下的总是多项数据,并且是在该行为发生的瞬间就必须由服务器传出了。在有着存储容量要求和存储响应及时性要求的这些前提下,势必不能对发生频繁的行为以及不重要的行为记录——发生频繁的行为会让服务器的处理负担以及存储空间的压力过大,而不重要的行为在记录后多半是白白徒占了很多服务器存储空间的。

所以基于以上原因,Log的涵盖范围界定在以下领域里:

换言之是遵循以下三条准则:

  1. 重要的行为尽可能Log;
  2. 不重要的行为尽可能不Log;
  3. 一般重要的行为视情况Log;

文本式vs.表格式

如前所说,Log分为文本和表格这两种格式。虽然文本格式的记录在第一时间打开后有着查阅方便的特征,但表格式相比于文本式有着太多的优势:

  • 从前面的例子可以看出,文本式的易读性是建立在极多的冗余文本的情况下的——真正有用的数据只是标记好的数据。这种冗余文本会带来很多问题:
    • 徒占大量存储空间。

这些文本在每一行记录里都重复存放,一条记录里占据了50%以上的冗余空间,让原本有限的存储空间平白被无用数据分割掉一半以上。

  • 耗费服务器处理机能。

为了这些冗余文本,服务器需要耗费额外的机能去写入这些信息。

  • 本土化增加额外工作。

这些冗余文本势必用一国文本书写,在游戏需要做海外版本时势必多了这部分的本土化工作。

  • 除此之外,表格式还比文本式的Log要更容易解析和处理,这在效率和成本上无疑又是多了一大优势的;
  • 为了让表格式Log增加易读性,只需要在需要查看Log记录的本地配置一个查看工具,在解释到各个字段后配以“冗余文本”来提高易读性就好了。

基于这些原因,我更建议用表格的格式来Log下游戏里的数据。

我们再来回顾一下Log涵盖的行为领域的这张图,

从频繁度来看,这部分的行为包括较为频繁和较为不频繁的两部分,对这两部分的数据,我们要采用两种不同的方式去Log。

两种不同的Log方式

对于不太频繁的行为,最简单的Log方式就是把每个触发该行为的玩家的完整行为都记录下来了。但对于较为频繁的行为,每个触发该行为的玩家都Log下是在处理负担和存储负担上都很大的。对于这类行为,我们要进一步抽取出行为中最重要的特例,只对这些特例进行Log。

我在这里举几个例子:

  • 例子1:物品流通——符合指定ID时记录

玩家有多种方式流通物品:例如打怪掉落拾取、商店购买/出售、玩家间交易(面对面交易、摆摊、拍卖行等)、主动拖出背包销毁、使用消耗、物品强化失败后的消失……等等。

这些情况单独来看,有部分行为是不太频繁的(例如拍卖行、主动销毁、强化失败消失等)。但所有总汇起来后物品的流通行为是很频繁的。

从游戏价值观来看,不是所有物品的流通情况都是有记录价值的。比方说灰色物品、大量消耗的血瓶等物品都没有记录价值。而部分物品的流通情况有着极高的追踪意义——例如顶级物品、需要花费RMB消耗的道具等。

所以对物品流通行为来说,更合理的方式是圈定少数重要且流通不频繁的物品来对此追踪记录。例如游戏里的套装、商城道具、顶级装备、强化到一定程度的装备等等。

  • 例子2:金钱变动——符合指定条件时记录

玩家的金钱变化是很频繁的:打怪拾取、买卖物品、修理强化、使用各种游戏功能、玩家间交易……有太多太多的行为会导致金钱变动。

但往往我们关注到金钱变动时最关心的是大笔的金额变动,所以对金钱变动行为来说,只需要限定一个阈值,当变动超出阈值时则把该项记录Log下来。

  • 例子3:战斗行为——仅对指定玩家记录

正如我们前面提到的,玩家的战斗行为也是极其频繁的——在1分钟内可能与十几只怪物战斗过,出手了几十招,每招都造成了不同伤害和不同效果。要把所有在线玩家的这些行为都记录下来是完全不可能的。

但如果我们指定玩家去追踪却是可能的。我们可以圈定一两个甚至一组数量不多的玩家,对其战斗行为进行详细记录,这样可以有助于积累一些战斗的特征样本,供日后研究。或者是在出现问题时单独追踪各个嫌疑人,看看能否从蛛丝马迹中找到犯罪证据和犯罪手法。

通过这两种不同的Log方式,我们可以把橙色区域里的行为都按不同程度地log下来。但接下来我们会产生另一个问题了:

Log什么信息?

我们该Log下什么信息呢?

对此我们首先想到的是Log下相关信息。但通过以下例子,我们会发现即使是有了“相关信息”这重过滤,这些相关的信息也是相当多的。

PvP战斗里一次出手的相关信息

仅以一场PvP战斗里的一次出手来说,至少分为时间、地点、人物和过程,那囊括的信息包括如下:

时间 出手时间(产生该行记录的时间)
地点 战斗地点
人物 攻击方身份及其所有属性(人物属性、临时状态、所有附属功能(例如背包、宠物)的当前状态)受击方身份及其所有属性
事件 攻击方出手技能及技能的消耗情况是否成功造成伤害(命中、闪避、格挡等)技能出手时长受击方受伤害的数值是否造成受击方死亡

虽然只是以上一个简表,但以上包含的数据项在一般游戏里都超过几百项,全部记录下来肯定是不可能的。

那除了“相关信息”,我们还需要什么样的过滤条件呢?

“有价值”且“由行为产生”的相关信息

什么叫“有价值”呢?我们来回看这张表:

时间 出手时间(产生该行记录的时间)
地点 战斗地点
人物 攻击方身份及其所有属性(人物属性、临时状态、所有附属功能(例如背包、宠物)的当前状态)受击方身份及其所有属性
事件 攻击方出手技能及技能的消耗情况是否成功造成伤害(命中、闪避、格挡等)技能出手时长受击方受伤害的数值是否造成受击方死亡

战斗时玩家的背包情况虽然是和攻击/受击方有关的,但对这场战斗的信息来说是无价值的,真正有价值的是时间、地点、人物身份和事件。

而“由行为产生”的是指这些信息是在该行为发生时才动态产生的。例如技能的消耗情况是固定的,不同技能有着固定的消耗值。这种固定不变的数据每次记录下来都是没用的,是一种早已得知本该如此的冗余信息。

经过“有价值”和“由行为产生”这两重过滤后,值得记录的信息只剩下这张表了:

时间 出手时间(产生该行记录的时间)
地点 战斗地点
人物 攻击/受击方身份
事件 攻击方出手技能是否成功造成伤害(命中、闪避、格挡等)受击方受伤害的数值是否造成受击方死亡

这就由原来的几百项数据减到只剩下8项数据了。

经验型数据

我们做了很大的减法了,貌似这次减去的又太多了。此时我们不禁问一句:除了这些信息就够了么?

是够了,但又可能不够。

例如我们来看看在战斗里的【攻击/受击方角色等级】这两项信息。

当从前面的“有价值”和“由行为产生”这两个指标来看,它们都是不符合的。参与战斗的角色等级和这场战斗乃至战斗中的一次出手没有太大的联系。但把这两项数据记录下来对后期的数据分析有着很大好处。

例如Log下了这两项我们能分析出以下情况:

  1. 玩家多数是在多少级以后才开始PvP?
  2. 玩家多数在等级差距多少时会主动地进行PvP?
  3. 当等级差距超过多少时会造成超过80%的必胜几率?
  4. 当等级差距相近时,获胜几率是否接近?
  5. ……等等

仅仅是多记录下两项数据就能带来如此多重要的分析结果,缺了这两项数据,我们好像基本无法从这个Log记录中得到多少有价值的分析。

我把这种称之为“经验型数据”。

在Log条目里加入经验型数据会直接左右着一类Log的价值程度,这种数据或者可以通过经验来加入进去,也可以在后期运营过程中想到时再想办法加进去。总的来说,虽然它们乍看是不相关的,但却是最关键的。

Log什么信息?

所以总结来看,在考虑Log一项行为时,需要Log下的信息是:

“有价值”且“由行为产生”的相关信息,再加入适量的经验型数据

多张Log表的横向考虑

一个游戏里要Log下的行为很多,当设计到多张Log表时,也需要在横向上作一定的考虑:

  1. 不同表的同一个数据,数据字段名必须一致

Log表格就像数据库那样,虽然不存在互相引用,但在后期进行数据挖掘和分析时需要进行视图的并、交、减等操作,借以更方便和自动化地生成报表和分析结果。

在这种情况下,不同表的同一种数据必须保持字段名一致,否则就会出现在视图操作时无法关联的情况。

例如玩家战斗Log和玩家等级提升Log的玩家ID字段都应该命名为PlayerID,而不应该一个叫PlayerID,另一个叫UserID。

  1. 同类行为尽量只在一张Log表里出现

这是考虑到Log的存放空间以及后期数据分析和数据挖掘的效率考虑的。同类行为只在一张Log表里出现,则Log数据的冗余度减到最小,在后期分析和挖掘时效率也更高,且更有针对性。

谈完了Log的分类和具体的设计,我们再来看看存放。

捕获的鱼的存放——Log备份

正如不准备鱼篓就无法享受捕鱼后的硕果那样,没有一套完善的Log备份方案,即便鱼群无一遗漏,最终还是会丧失无遗——因为游戏运营的时间不是一天两天的。日复一日年复一年后,再巧妙的数据记录方法都终会让存储空间撑满,而后不得不日复一日年复一年地丢弃过去的数据。在这存放和丢弃的艺术间如果没有做到合理的平衡,那势必会造成最大的损失。所以我们不得不顺带谈谈Log的备份了。

Log是一种事件和行为触发的记录,事件行为在时时刻刻都有可能发生。但考虑到统计和分析的惯例,Log记录一般会在每天或者每周备份一次。

Log记录在备份后会进行压缩、挪移和删除,一般来说会遵循以下三种原则:

  • 对于最近Log下的记录(例如3个月内),完整地保存这些备份记录。便于随时能快速检索查阅,以便于对这些较新的的数据进行提取、分析和挖掘;
  • 对于还具有时效性的记录(例如1年内),对这些记录进行压缩。让服务器还有访问和利用这些数据的可能性,但访问所需的时间较长;
  • 对于早已不具时效性的记录(例如1年以外),对记录进行压缩或者挪移到别的地方备份,不再对其进行访问和利用;

随着存储空间的日益增大,以上时间范围也会不断扩大(当然,这里写的数字本来也只是随便一填,便于让大家更容易理解而已)。

经过了捕获和存放,我们已经把相当一部分的鱼群捕获了,那剩下的呢?

累加器

回看这张图

Log只是捕获了如上橙色区域的鱼群,纵然说不重要的行为是完全不需要考虑的,但我们还有着为数不少的漏网之鱼。

看来还需要别的渔网来补充。

什么是累加器?

累加器是一种会不断地做加减法的变量(主要做加法)。这些变量存放在服务器缓存里,由服务器动态不断维护,并在固定间隔时间(例如每天定点)里备份并清零一次。

游戏里有很多行为都是跟同一项数据有关的,或者说所有这些行为都交集在同一项数据上。而偏偏很多时候我们最需要分析和捕获的又正是这项数据,对于这种情况,我们完全可以用累加器的方式来记录。

累加器能解决两种问题:

  1. 当行为发生的频率很频繁时;
  2. 且这多项行为都交集在同一项数据上,而这项数据正是我们最关心的;

换言之,累加器能稀疏地覆盖以下领域:

两个例子

或许我们举两个常见的例子就能明白累加器的作用了。

例子1:服务器每天产出的金币。

游戏里很多种行为都会导致服务器产出金币——所谓“产出”,也就是服务器无中生有地产生金币到玩家手上(但不包括玩家与玩家间的流通)。我们来简单列举一个通常的网游里常见的导致金币产出的行为:

  • 怪物死亡掉落金钱后拾取;
  • 物品贩卖到NPC商店里;
  • 完成任务后获得任务奖励金钱;
  • 游戏里各种系统赠送的金钱;
  • 游戏里通过商城购买换取的金钱;
  • ……等等

当任何一个玩家触发了以上行为并产生一笔金钱时,服务器把金钱数额累加到当天“服务器产出金币”的累加变量上,如此一天下来就知道当天服务器总共产出了多少金钱了。

附带一提,用这种方法也能统计出服务器每天从玩家手上消耗掉的金币。通过【服务器每天产出的金币】和【服务器每天消耗的金币】在一段时间内的对比,就可以知道该服务器的经济情况是趋于稳定、越来越紧缩,还是越来越趋于通货膨胀了。

例子2:单种怪物每天消耗数。

服务器各种怪物每天都会被玩家不断地虐杀。假如能统计出每种怪物每天被虐杀的次数/只数,那或许对怪物的数值改良或者关卡设计改良有很多帮助。

对此服务器只要为游戏里每一类怪物设置一个累加变量,当该类怪物死亡时累加器+1,如此一天后就能得到每一类怪物在当天的消耗数了。

以此类推,还可以统计每张地图一天内的进出人数,乃至到游戏里每个城市特定时间段里的进出人流量。

由以上例子看出,累加器机制对Log机制是很重要的补充,而且捕获的“鱼群”都是很单一很有针对性的。既然如此,那我们来看看这种渔网到底是如何构成的:

累加器的原理和做法

累加器的机制实际上是在服务器内存里为每一个需要累加的数据项预留一个累加变量。服务器内部记录了该数据项的各种累加条件,每当任何一个玩家触发游戏行为,达成其中一种累加条件时,则把该行为里的数据项累加到相应的累加变量上。

由于这些数据通常都是统计每天的情况,所以各个累加器会在指定周期(例如每天)的定点备份一次,然后清零,再重新累加。

有了累加器的帮助后,貌似我们需要捕获的数据领域都已经涵盖了。但其实还不够——频繁发生的行为通过累加器只能捕获到很稀少的一部分,我们还需要借助最后一种渔网才能把玩家行为都尽数捕获。

客户端Trace

什么是客户端Trace?

客户端Trace只是一个构思,还没真正实现过,仅作为本文的一种讨论项。

所谓的客户端Trace是指由本地客户端去跟踪和记录一些很频繁的行为,这些行为包括以下两者:

  1. 客户端传到服务器的,服务器能判定到——却因为行为发生得太频繁,考虑到服务器的负载,无法由服务器来记录的行为;
  2. 真实在客户端前发生,但却如我们前面所说的被客户端屏蔽,没传到服务器上的行为;

以一场战斗为例,玩家在技能CT和CD时间内敲下的按键是不会传到服务器(第2类行为)里的,但即便是传到服务器经服务器判定的技能施放行为,在短短一场战斗里也相当频繁(第1类行为)。

换言之,客户端Trace能涵盖以下领域:

除此之外,客户端Trace能做到的还不仅是这点。

在客户端里有很多信息是只在本地记录和读取的,但这些记录对产品改良和易用性改良有着极大帮助——例如玩家本地客户端的系统配置、自定义的操作按键配置,以及玩家本地的机器配置信息。

这些信息用客户端Trace的方法也能捕获到,为产品改良带来极大帮助。

把客户端Trace说得这么神奇,那这种渔网的结构又是怎么样呢?

客户端Trace的渔网结构

虽然客户端Trace分为两种,但其总体原理都是类似的:

用户/玩家在客户端前操作,其行为和信息在客户端前除了上传给服务器以外,还在本地维护一份Trace记录。在玩家每次下线时,这些客户端Trace记录会上传到负责数据积累和提取的服务器。

从客户端Trace的两个服务方向来看,在具体做法上又分为两种:

行为式客户端Trace

所谓行为式客户端Trace是指对玩家的操作行为进行记录,这种记录往往是针对单个账号的单个角色的。其记录的原理、过程和格式都和Log机制基本一样,只是它是记录在客户端本地并经过加密(以防被利用来做外挂)的,在玩家每次退出角色时上传到数据提取服务器,由该服务器对这些记录进行自动提取和汇总。特别地,为了让数据提取服务器能区分开各个角色,这些上传的记录会带上玩家的角色ID号。

与此相应地,另一种做法是

配置式客户端Trace

配置式客户端Trace是指把玩家在本地客户端的配置在每次下线关闭客户端后自动上传到数据提取服务器。通常来说客户端的配置都是针对单个客户端的(也有一些游戏是针对单个账号乃至单个角色),此时需要在上传的记录里带上机器的MAC地址,便于信息的唯一性校验。

或许只是这么看还有点抽象,我们来看几个实际的例子好了。

客户端Trace实例

我们接下来通过几个实例来了解客户端Trace的原理和作用。

例子1:玩家战斗记录

前面我们说到过玩家的战斗过程是会产生大量的战斗信息的,这些记录如果全部log下来是不可能的,无论对服务器性能还是空间负载都是一个极大的考验。

但假如这类记录通过客户端Trace的方式记录下来就完全没问题了。在战斗过程中,每个玩家本地的客户端都把玩家出手的相关数据记录下来。例如:

  • 玩家等级
  • 玩家职业
  • 使用技能ID
  • 目标类型(怪物/玩家)
  • 目标等级
  • 这场战斗里第一次出手?
  • 伤害情况

当玩家退出该角色时,客户端自动把这份记录上传到数据提取服务器,由服务器来进行提取、分析和汇总。例如得知每个玩家的以上记录能提取和分析出以下结果:

  • 某个职业在某个等级下各种技能的使用比例如何;
  • 各个等级下的玩家引发PvP的分布如何;
  • 在战斗中玩家与怪物的等级差距的分布如何;
  • ……等等

当然,还能分析出前面我们提到的那些结果:

  1. 玩家多数是在多少级以后才开始PvP?
  2. 玩家多数在等级差距多少时会主动地进行PvP?
  3. 当等级差距超过多少时会造成超过80%的必胜几率?
  4. 当等级差距相近时,获胜几率是否接近?
  5. ……等等

例子2:玩家按键记录

在一场战斗里,记录如前的信息能统计战斗中的情况,借以分析战斗系统、技能系统和职业系统中的缺陷之处,而后有针对性地改良。

但另一种记录方式能为另一块领域带来截然不同的帮助——记录玩家按键情况。

正如我们前面所说,当技能处于CT和CD时间时,玩家的按键是不会传到服务器的,但这些按键操作在记录下来后对手感和易用性的改良有很大帮助。一定程度上可以这么说,当玩家不断尝试敲击按键却在屏幕上没有反馈时,此时玩家感觉的手感是很差的;相反,假如玩家胸有成竹地逐一敲键,而屏幕上也做出有节奏的符合玩家预期的反馈,那他会觉得手感很好。

当记录下玩家在客户端的真实按键情况(这种记录可以只是对每种技能做简单的计数统计)时,我们可以用真正传出去到服务器的技能施放次数来比较。两个数据越接近的技能一定程度上说明它的手感越好、时间把控越好;两个数据相差越远的技能说明玩家对这个技能的施放感到焦躁不安。

此外我们也可以通过看各个等级段的玩家在这方面的操作情况,以此来了解玩家对游戏的熟悉情况(这对于偏操作的动作类游戏来说尤其有用)。

例子3:游戏图像配置

每个游戏都会考虑不同玩家的性能,在图像配置上预留多种不同的档次供玩家选择(尤其是对3D游戏来说)。玩家在选择后往往这些配置会存在客户端本地。

通过客户端Trace能把每个玩家的图像配置选择情况上传到数据分析服务器,服务器通过MAC地址保证统计的唯一性,由此我们能知道很多信息:

  • 各档图像质量的使用发布比率是如何的?
  • 在特效细节、贴图细节、模型细节里,玩家最在乎哪一种的细节?
  • 玩家通常会把景深开得多远?
  • 自由镜头和视角锁定这两种摄像机模式哪种最受玩家欢迎?
  • 对于画面的后期处理风格(例如天下2),玩家更喜欢哪种风格?
  • ……等等

通过了解这些信息,我们可以针对性地改良游戏画面:

  • 例如玩家很重视特效细节,那就在特效上增加更多的细节表现;
  • 例如玩家一般不会把景深开得很远,那可能是因为开远了后太卡或许是根本不需要做很远的景深处理;
  • 例如玩家喜欢用视角锁定,那就基于这个镜头来把画面做得更精细;

除此之外,客户端Trace还可以读取玩家的显卡的硬件信息,把该信息上传到服务器作为数据统计。

不得不再说一下,客户端Trace只是自己的一个初步构思。可能有着很多不成熟的地方和难以解决的问题。该构思只是提出来供大家讨论分析罢了。

看过了玩家行为的捕获,接下来我们来看看瞬时状态的捕获。

µ    瞬时状态的捕获                                

两种的瞬时状态

游戏里的瞬时状态分为两种:

  1. DB历史数据

DB里的数据都是由玩家产生的。当玩家做各类行为时会对这些数据进行改变,改变后的数据覆写到DB里就会让原有的数据被永远埋没了。

例如玩家背包里的物品是会在一次上线的游戏过程中不断改变的;又例如玩家的等级都是不断成长的,这个值会不断地变动。

所以说DB的历史数据都是不断变化的——不断地产生新状态,不断地把前一刻的状态覆盖。

  1. 服务器瞬时状态

服务器里的所有玩家就像一个庞大的社会群落那样。他们每天每时每刻都会上演千姿万态的情景,这些情景是稍纵即逝的。

例如某一次国战里的参战人数和参战人员信息;又例如某一时刻单张地图的在线人数。

对这种游戏世界里上演着的百态,每一个瞬间的情景都是完全不同的。

对于这两种瞬时状态,我们需要针对性地去捕获。

拍照,而不是捕鱼

如果说前面玩家行为的捕获相当于是把鱼群全数捕获的话,那瞬时状态的捕获更像是对鱼群进行拍照——一张张地照下它们瞬间的状态。

  • 对于DB历史数据,我们需要采用DB快照的方法。在每天的固定时刻点对DB进行快照备份
  • 对于服务器瞬时状态,我们需要用服务器定点主动统计的方法。在每天的固定时刻点对服务器当前的瞬时状态进行主动统计

接下来我们展开来详细谈谈这两种渔网的结构和捕鱼方式。

DB快照

谈到DB快照,我们可能会引发一个问题:

对DB进行快照有什么用呢?

那或者我们来换一个问题思考:每天的新闻报纸有什么用呢?

从我看来,新闻报纸有着以下两个用途:

  • 记下当天发生的要事,供日后可以查证和引用;
  • 公开当天发生的要事,让大家能更容易获知;

DB快照也同样起着这两个作用:

  • 拍下当天几个固定时刻的情况,供日后可以查证和推理出一些有用的结论;
  • 拍下情况后由分析服务器来提取分析,得知当天游戏里出现的一些重大事件或重大问题,让大家能更一目了然地清楚情况;

我们具体来看两个例子就明白了。

DB快照实例

例子1:拍下等级演变情况:

对于一组新开的服务器,我们只要拍下每一天玩家的等级分布情况,在一个月后我们就能得到整个服务器的等级演变情况了。

借此我们能了解到:

  • 各个等级段的玩家的分布情况如何?
  • 服务器有没有出现老化趋势(高级和低级的人数比例越来越大)?
  • 在哪些日子或时刻玩家的等级提升最快?是节假日导致呢?还是特殊的活动导致的?
  • 整个服务器的等级成长比预期更快还是更慢?
  • 哪些职业的等级成长相对过快?
  • ……等等

借助这些了解,我们可以相应地改变游戏内容,让游戏的等级成长更为合理。

例子2:拍下价值物演变情况:

游戏里有着很多价值高昂的物品,例如顶级装备、强化到最高的装备、商城昂贵的道具等等。

通过每天的DB快照,我们能得出这些价值物在数量上的演变情况,从而了解该服务器的价值物产出速度,借以判断是否符合我们的预期,是到底过快了导致这些价值物的稀缺度削减了,还是过慢了让玩家丧失了追求的动力了。

通过这些了解,我们可以相应地改变这些价值物的产出情况,给玩家最合适的动力去刺激他们。

巧妙地拍照

对一个几十万人同时在线的产品来说,DB的量是很可怕的。每天要对这么庞大的DB进行多次的快照,这一定有着很多技术困难和存储压力。

我对动画很感兴趣,有空常常会去了解它的制作方式。

在动画制作里有两个技巧:

  1. 把握关键帧。也就是画面发生明显变动的时刻,该时刻的画面是最关键的,最能展现出一段动画的视觉变化的。
  2. 只绘制变化的部分。静止不变的部分如果重画一遍是费心费力的,动画师一般都只对变化的部分绘制而已,静止不变的背景只画一两次就够了。

引申到DB快照,在对DB拍照时也需要掌握这两个技巧:

  1. 什么时候拍?
  2. 拍什么?

什么时候拍?

首先要掌握的是拍照的时间点。

对于不同类型的数据,拍照的频率也是稍有不同的:

  • 像公会等每天变动不频繁的“景色”,只需要一天拍照一两次就可以了;
  • 但像玩家背包这些变动很频繁的数据,一天可能需要在很多个时刻里拍照;

此外,拍照挑选的时机也是很重要的。

  • 凌晨玩家最少的时候固然是一个好的拍照时刻,因为此时景色相对固定,是一个取景很准的时间点;
  • 但在一段人少的时机里连续拍两次无疑是不理智的,因为这段时间内景色的变化很少,两张照片基本类似,平白浪费了相册空间;
  • 比较合适的是在每一个人数高峰期的顶峰过去后进行拍照,这时候有着最明显的景色变化,一天内拍出来的照片也更多变更有价值;

除了什么时候拍,拍什么也是很重要的。

拍什么?

正如动画师只挑变化的景色绘制那样,效能最高且最节省存放空间的方式也是只拍那些变化的数据。

通常一种方法是对上线用户的数据进行拍照。

我们可以在用户上线、最后一次下线和上线过程中每隔一段时间进行拍照,这样能得到每个用户在上线过程中的状态变化,同时也把胶片用在最有用的景色上——那些不曾上线的角色的数据是不大会改变的,不断拍这些景色是徒耗资源的。

DB快照的“相册”

就像捕鱼需要鱼篓那样,拍出来的照片还需要相册去存放。DB快照也需要经常性地备份和处理。

其备份和处理的机制和Log是类似的:

  • 对于最近存下的快照(例如3个月内),完整地保存这些备份记录。便于随时能快速检索查阅,以便于对这些较新的的数据进行提取、分析和挖掘;
  • 对于还具有时效性的快照(例如1年内),对这些记录进行压缩。让服务器还有访问和利用这些数据的可能性,但访问所需的时间较长;
  • 对于早已不具时效性的快照(例如1年以外),对记录进行压缩或者挪移到别的地方备份,不再对其进行访问和利用;

通过勤快地整理手中的照片,我们能把里面很多有用的景色提取出来,成为我们分析和改良的利器。

我们再简单看看服务器瞬时状态的拍照。

服务器定时主动统计

服务器主动统计的瞬时状态都是很有针对性的,就像我们前面说到的实例那样,我们会通过这种方法来拍下某个时刻内玩家的数量和状态,借以观察这次事件的情况。

例如在国战开始后,服务器主动统计此次战争双方的参与人数,统计出这些人的等级分布情况。在战争结束后,统计双方的残余人数。通过这些数据我们能了解每一次国战的部分战斗情况,借以改良国战的方式或者国战地图的关卡设计。

服务器定时主动统计更多是制定一套规则来让服务器在满足条件时主动去统计出相应的数据——无论是规则还是统计的数据项都是极有针对性的。这种方法更多不是像Verycd囤资源那样日后再看有没有用了,而是很有目的性地提出一些统计方法,祈求通过统计结果来了解情况,改良游戏。

µ    完全捕鱼手册                                  

迄今为止,我们已经看过了四种不同的渔网了。我们看过了它们的构成、运用领域、实例和一些简单技巧。这四种渔网是服务于不同领域的:前两种能帮助我们把鱼群捕获,后两种能让我们拍下鱼群精彩的瞬间。通过探讨这四种不同的渔网,我们已经翻阅完一本简单的完全捕鱼手册了。

然后呢?

捕来的鱼和拍下的照片肯定是有很多用途的,它们会用来分析,用来挖掘,用来改良游戏。但这些都不在本文讨论范围内了,本文只是一本粗略的完全捕鱼手册,一起去探讨捕鱼的简单入门指南,为之后的鱼的挑选、加工、处理和享用作了最重要的铺垫。

当然,文中也有捎上一些简单的技巧(或说不入眼的小伎俩),这些技巧和此后的数据监控、分析、挖掘和改良是密切相关的,只是基于本文定位不作深入而已。

µ    后记                                          

虽然入行这5年来都从事研发,但我一直认为游戏运营过程中产生的数据是对当前以及以后的产品都有着极大作用的。也因为这个理由,从去年到今年,冒着荼毒众生的风险来写下了两篇文章。

虽说数据监控、分析、挖掘和指引改良已经成为行业内日渐成熟的领域了,但我认为再成熟的数据处理阶段都是建立在有数据的基础上的。或许说从监控到给出改良建议这些步骤都是有经验的运营人员可以实现且负责的领域,那数据的积累和沉淀呢?

九城在过去多是以运营为主,这些从国外漂洋过海登陆的产品都在数据的积累和沉淀上做得很不体贴,或许代理的国外产品都有着这种通病——要不是没能积累什么数据,要不就是有着很强的积累和沉淀数据的方式,但这些数据都牢牢控在手里不愿意给运营方。

但这对产品的运营来说是好事吗?

当然不是好事。正如我去年的文章里提到的,我相信沉积下来的数据能带来很多结论,这些结论对产品是突破性和革新性的,完全能把产品进一步改良成一流的范例。

基于这些理由,在这个续作里我选择仅仅是深入地谈数据积累了——这是产品改良的前提,也是项目在研发阶段就能铺设好的利器。

最后顺带提一个小插曲:

这篇文章从最初开始着手时经历了两版。最初一版是打算基于去年文章的基础来深入全面地展开的。大纲完成后写了7、8页发现文章铺得太开,很难把控,也整篇文章很松散,于是把大纲连同已写好的几千字都全部丢弃,重新构思重新撰写,最终得出本文——一篇只谈捕鱼的文章。

希望这篇文章能对研发和运营的人员在产品改良的方法上带来指向,更希望这篇文章能促使今后各个产品的珍贵数据都保留下来,借以诞生出更棒的游戏体验。

欢迎就这篇文章后续讨论。

如若转载,请注明出处:http://www.gamelook.com.cn/2010/02/11832

关注微信