腾讯 iData 分析中心是 iData 产品的重要组成部分之一,负责号码包提取、画像分析、工作分析等围绕用户号码包的数据分析功能。在长达几年的运营之后,针对运营中产生的一些问题和用户的新需求,我们意识到了旧系统的不足,开始打造新的分析中心后台。本文开始将以系列文章,围绕新分析中心后台 TGMars 的计算平台的方方面面,来探讨、介绍我们是如何思考、研发新分析中心的。
旧的不去、新的不来
接手旧项目,抗拒估计是肯定的,里面可能会含有大量祖传代码,复杂的意大利面代码,好像合理但是似乎又不合理的架构。
不过肯定不能这么偏见的认为事情一定是这个样子的,因为口碑毕竟不错,外在的效果还挺好,完全不是那种所谓“失败项目”。
腾讯 iData 分析中心的确是一个很好的项目,不管是号码包提取还是跟踪分析还是画像分析,真的很快。但是快就够了么?没有槽点么?
看到的和实际总是不一样。
“我想能有一个新的需求,这个应该很简单,就是扩展一下维度就好了”,面对这种问题,答案往往是需要痛苦的扩展已有的计算逻辑,写大量的代码,从上到下。
“你们就支持提取一些基础指标,游戏里面有些跟踪指标我想定制”,如果一个一个定制可能要排期很久了。
“今天的位图更新失败了,是什么原因呢?”常常有这种情况发生,但是原因可能多种多样,处理方案总结起来可能要和 737 的故障手册图一样写一本书,很难交接到运维。
总之运营过程中遇到的不少问题,无法回避,总结起来却无外乎几点:
计算难以定制,功能难以扩展。
架构复杂,责任不清晰。
部分模块有短板,常常不稳定。
难以运维。
每个点都要展开去讲太难,既然没到软件工程专家的境界,也没资格去长篇大论的阐述每个地方如何做才能完美;当然也不能只是抱怨一通,然后什么都不做改变。模块代码质量或者有 Bug 不必多谈,如果下功夫修复肯定能解决,但是既然决定了我们要做新的系统,解决之前线上我们遇到的各种问题,那么不妨先只谈当前看到的问题,思考下以后该怎么规避。
为什么这么快?为什么难以扩展?
1. 为什么这么快之位图存储
众所周知,腾讯 iData 分析系统很快的秘诀在于位图。那么我们是如何用位图的呢?
游戏用户的最最基础且重要的行为,往往有几个:注册、登陆、充值和消费。注册和登录时用户进入到游戏,正在玩这个游戏的标志;充值和消费是花钱了,游戏创造了收入的标志。跟踪分析就是跟踪这群用户是否还在玩,是否还在花钱。既然是最关心的需求,那么就要做到最高的支持,就按照这几个行为来做。
位图是一个好又简单的数据结构,可以直接以 0 或 1 来标示用户是否产生了某些行为,0 或 1 构成的数组是有顺序的,顺序正好可以表示为时间维度。
一个用户对应一些定长的数组,表示用户当天具体行为,固定为 396 位,头 12 位为月度行为特征,后 368 位为具体到日期的行为特征,后面冗余 8 位没有使用,方便位移和对齐。
每个用户的位图,其首位表示的日期是不同的,有一个最近日期的概念,这是为了方便每次更新位图的时候不用全量移位所有的位图。同时一个用户有多个位图,可以同时知道用户的多个行为,不用再多表关联,可以知道是否登录的同时还知道是否有过消费。因此位图文件的结构按下图所示。
用户记录按一个固定规则进行分片:如果号码类型是数字的则余分片数,如果是字符串的则取 CityHash 再余分片数,账号基本上是均匀分布到每个分片的。
2. 为什么难以扩展之行式存储
位图设计很有效,但是位图文件本身的结构仍有问题:
行存储,位图文件大小是固定的;
位图分片固定,且一旦生成整个生命周期都是固定的,导致位图文件在集群上的分布总是不均匀;
难以扩展,整个结构都是预先设计好的。
行存储的优劣很多文章都有谈到,主要是 OLTP(On-Line Transaction Processing,联机事务处理)用行存储更好,毕竟需要频繁删减的数据库,如果需要快速查询另外附加索引便好,而劣势则是对于 OLAP(On-Line Analytical Processing,联机分析处理)来说,如果只需要几个列的内容,没必要一次扫描那么多数据。
iData 分析系统正好是 OLAP 的:
实时性要求没那么高,毕竟只是查询为主。
数据量大,普通的数据库难以承载,游戏一火爆轻松上亿。
系统主要支持决策,用户可能有自己的想法去提取数据,计算可能还有些复杂,不是预先设计些索引就满足的。
更重要的,数据输出量大,跟踪分析还好,提包需要提取大量记录出来。
因此固定的行式存储,仍有不足之处:难以扩展,不太好适应需求。
3. 为什么这么快之 MapReduce 架构
看一下模块结构图:
计算过程大体需要让用户分群对应的号码包通过包分片服务按相同规则分片,下发到位图和 ETL 文件相同的节点上;而号码包和位图的匹配是一次 Join 计算,精确来说是 SortMergeJoin,即号码包分片和位图分片都有排序,排序后的内容一次 Merge 便能匹配,Merge 是 O(n)的复杂度,而号码包排序显然比预先排序好的位图要快很多,因此计算复杂度不大。
在架构的实现上则是一个典型的 MapReduce 结构,Map 在两个地方生效,包分片将号码包按同样的分片规则分片,而 Map 后的结果在计算节点和存储的位图在本地聚合,没有任何多余的 Shuffle 操作,避免了网络开销;而 Reduce 则在包合并的过程中重新汇总为号码包,或者把统计结果汇总到数据库。
可以看出快的秘诀在于存储计算绑定,相同的分片规则使得两表的 Join 没有任何 Shuffle。
4. 为什么难以扩展之 MapReduce 架构
老实说 MapReduce 有些过时了,他毕竟代表了 Hadoop 的那个荣光时代。想想当年怎么在 Hadoop 上写程序的:
写一个 Mapper,把单个分片的内容汇总成单词和频率的键值对。
写一个 Reducer,把键值对汇总。
然后我们就会发现 Map 很快,Reduce 很慢,因为 Reduce 是在一个机器上做的。
在位图分析系统这里,就是包合并模块,这个模块是经常出错的罪魁祸首,往往有 Bug 或者承载超过负荷就导致任务重试或失败,苦不堪言。
另外所有的任务都适合 MapReduce 么?复杂的 SQL 能支持么?Join 的时候需要涉及到多表的时候可以么?不行,这里只支持一次 MapReduce,一次结果直接写号码包或者到数据库了,没有第二次机会。
因此 MapReduce 的计算架构,扩展性较差,任务的计算粒度不能再分了,无法实现太复杂的需求。
跳出既有的思维模式
出现这样的情况,再分析一下,可能是这样的原因,以往我们以游戏数据分析为中心考虑问题,使得我们的思维受到了局限:第一层次是业务,业务下有数据,按照类型分为登录/注册/充值;提取的指标对应一段计算代码,它负责按照指标蕴含的含义做相应计算动作。
可是转念一想,这不就是一个数据库么?业务是数据库,登录/注册/充值是数据表,为什么到了这里就不分了呢?提取动作就是 SQL 计算,为什么到这里就是固定的代码了呢?
因此我们可以认为难以扩展的根本原因在于过于特化的设计一个系统。我们的目标不在于只是支持特定的业务场景,也不是特定的几种数据,应当抽象出来,按照一个 OLAP 平台的设计思路去做,跳出既有的思维模式;这也是后来面对平台开放后遇到的问题,我们的客户可能不仅是游戏相关的了。
我们需要一个什么样的计算平台?
在新分析中心立项的时候,我们对计算的部分立下了两个大的目标:
存储的结构可扩展。
计算逻辑可定制。
同时也稍微放宽了点要求,前期可以出结果稍微慢点,非常感谢这点——否则可能会不知所措。
先取其精华,总结下好的地方在哪里:
主键列按固定规则分片,方便匹配到记录。
存储计算绑定,没有 Shuffle 过程,减小网络开销。
位图,将时间维度横向转制,快速匹配到用户行为。
再去其糟粕,再看下哪里可以做得更好:
行式存储,是否可以改成列式结构方便只读取想要的数据?
维度扩展,是否可以是可定义存储结构?
支持更复杂的计算模型,是否支持多次的迭代?
支持 SQL,使得计算可以更随心所欲?
这就是新分析中心后台中计算部分的基本设计思路了:在好的基本设计思路下,我们还能做什么解决已有的问题。
为什么不直接用现成的?
“那为什么不直接用现成的啊?”,的确现有的很多大数据计算平台好像都满足需求,满足 OLAP 的能力,还能有更多。比如 TiDB、比如 Spark、比如 ClickHouse 或 Palo。
本文转载自公众号云加社区(ID:QcloudCommunity)。
原文链接:
https://mp.weixin.qq.com/s/nRYKqgdIS4E-Aexp6uJyvA
评论