大厂架构:每天数百亿用户行为数据,美团怎么实现秒级转化分析?( 三 )


文章插图
 
通过上述的讨论和设计,完整的算法如下图所示 。其中的核心要点是先通过UUID集合做快速的过滤,再对过滤后的UUID分别做时间戳的匹配,同时上一层节点输出也作为下一层节点的输入,由此达到快速过滤的目的 。

大厂架构:每天数百亿用户行为数据,美团怎么实现秒级转化分析?

文章插图
 
工程实现和优化
有了明确的算法思路,接下来再看看工程如何落地 。
首先明确的是需要一个分布式的服务,主要包括接口服务、计算框架和文件系统三部分 。其中接口服务用于接收查询请求,分析请求并生成实际的查询逻辑;计算框架用于分布式的执行查询逻辑;文件系统存储实际的索引数据,用于响应具体的查询 。
这里简单谈一下架构选型的方法论,主要有四点:简单、成熟、可控、可调 。
1.简单 。不管是架构设计,还是逻辑复杂度和运维成本,都希望尽可能简单 。这样的系统可以快速落地,也比较容易掌控 。
2.成熟 。评估一个系统是否成熟有很多方面,比如社区是否活跃,项目是否有明确的发展规划并能持续落地推进?再比如业界有没有足够多的成功案例,实际应用效果如何?一个成熟的系统在落地时的问题相对较少,出现问题也能参考其它案例比较容易的解决,从而很大程度上降低了整体系统的风险 。
3.可控 。如果一个系统持续保持黑盒的状态,那只能是被动的使用,出了问题也很难解决 。反之现在有很多的开源项目,可以拿到完整的代码,这样就可以有更强的掌控力,不管是问题的定位解决,还是修改、定制、优化等,都更容易实现 。
4.可调 。一个设计良好的系统,在架构上一定是分层和模块化的,且有合理的抽象 。在这样的架构下,针对其中一些逻辑做进一步定制或替换时就比较方便,不需要对代码做大范围的改动,降低了改造成本和出错概率 。
基于上述的选型思路,服务的三个核心架构分别选择了Spring,Spark和Alluxio 。其中Spring的应用非常广泛,在实际案例和文档上都非常丰富,很容易落地实现;Spark本身是一个非常优秀的分布式计算框架,目前团队对Spark有很强的掌控力,调优经验也很丰富,这样只需要专注在计算逻辑的开发即可;Alluxio相对HDFS或HBase来说更加轻量,同时支持包括内存在内的多层异构存储,这些特性可能会在后续优化中得到利用 。
在具体的部署方式上,Spring Server单独启动,Spark和Alluxio都采用Standalone模式,且两个服务的slave节点在物理机上共同部署 。Spring进程中通过SparkContext维持一个Spark长作业,这样接到查询请求后可以快速提交逻辑,避免了申请节点资源和启动Executor的时间开销 。
大厂架构:每天数百亿用户行为数据,美团怎么实现秒级转化分析?

文章插图
 
上述架构通过对数据的合理分区和资源的并发利用,可以实现一个查询请求在几分钟内完成 。相对原来的几个小时有了很大改观,但还是不能满足交互式分析的需求,因此还需要做进一步的优化 。
1.本地化调度 。存储和计算分离的架构中这是常见的一种优化手段 。以下图为例,某个节点上task读取的数据在另外节点上,这样就产生了跨机器的访问,在并发度很大时对网络IO带来了很大压力 。如果通过本地化调度,把计算调度到数据的同一节点上执行,就可以避免这个问题 。实现本地化调度的前提是有包含数据位置信息的元数据,以及计算框架的支持,这两点在Alluxio和Spark中都很容易做到 。
大厂架构:每天数百亿用户行为数据,美团怎么实现秒级转化分析?

文章插图
 
2.内存映射 。常规实现中,数据需要从磁盘拷贝到JVM的内存中,这会带来两个问题 。一是拷贝的时间很长,几百MB的数据对CPU时间的占用非常可观;二是JVM的内存压力很大,带来GC等一系列的问题 。通过mmap等内存映射的方式,数据可以直接读取,不需要再进JVM,这样就很好的解决了上述的两个问题 。
大厂架构:每天数百亿用户行为数据,美团怎么实现秒级转化分析?

文章插图
 
3.Unsafe调用 。由于大部分的数据通过ByteBuffer访问,这里带来的额外开销对最终性能也有很大影响 。JAVA lib中的ByteBuffer访问接口是非常安全的,但安全也意味着低效,一次访问会有很多次的边界检查,而且多层函数的调用也有很多额外开销 。如果访问逻辑相对简单,对数据边界控制很有信心的情况下,可以直接调用native方法,绕过上述的一系列额外检查和函数调用 。这种用法在很多系统中也被广泛采用,比如Presto和Spark都有类似的优化方法 。


推荐阅读