V8 JS AOT化的探索与实践

Posted Alibaba F2E

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了V8 JS AOT化的探索与实践相关的知识,希望对你有一定的参考价值。

JS 语言的动态性非常优秀,其弱类型等语言特性也使得一线业务开发者更容易上手,但这也导致 JS 每一次运行前都要重复编译,使得 JS 的执行性能不理想;虽然之前 UC 内核有做过 Code Cache 方案,但支持的场景不够完整,与原生 Native 的技术方案比,尤其是首次启动场景(如各类大促活动等)还是有比较大的差距。为了能尽可能做到与 Native 对标,缩小性能差距,同时让业务开发者无感,我们开发了 JS AOT 功能。本分享将结合目前集团内自有业务形态,以及 JS 在 Web 中的执行过程,介绍JS AOT是如何设计和实现的,以及能给业务带来哪些收益。本篇分享来自阿里巴巴的喻世江在第十六届D2前端技术论坛的分享。

附:第十六届D2前端技术论坛现场分享视频
背景

随着Web技术的不断发展壮大,javascript作为Web的逻辑开发语言,也凭借其简单易用等特性以及强大的生态,成为行业内最受欢迎的编程语言之一。V8作为JavaScript最强大的虚拟机引擎,在性能、稳定性、内存等各方面的指标受到业务的关注。

U4内核在V8引擎中深耕多年,进行了多方面的优化和能力扩展,为了使集团业务在Web快速发展,消除在使用Web应用时的各种问题,我们U4内核团队从快、强、稳三个方向,在 V8 引擎中做了大量的优化和能力拓展:

    1. 磁盘代码缓存技术:避免JS非首次执行时的重复编译,使JS性能提升50%以上;
    2. UC LLVM优化编辑器技术:为V8字节码处理程序生成更高效紧凑的汇编指令,使JS性能提升10%-30%;
    1. JS卡死检测:当JS出现卡死情况时,在native中可以得到告警并获取卡死时的现场信息,使业务尽早发现、定位和解决问题;
    2. OOM定位信息:在崩溃日志中增加了JS堆内存信息和OOM时的JS现场信息,使业务可以快速定位OOM问题;
    3. JS API扩展:在 JS中扩展了一系列能力,让H5开发者可以快速开发调试以及进行性能分析等;
    4. JSI:让业务可以脱离WebView使用V8,同时享有U4针对V8的所有优化成果;
    1. 疑难崩溃攻克:解决了线上发现的许多疑难崩溃稳定性问题,如 CPU 缓存刷新问题、高通部分 CPU 缺页中断处理BUG等系列非常底层的问题;
    2. 安全漏洞修复:修复各种 N-day、0-day 的安全漏洞并及时上线。


U4内核在V8引擎中做了很多优化和能力扩展,然而在将JavaScript和传统的Native技术相比时,仍然发现一些不足。

如下图所示,JavaScript在跨平台和动态化方面较Native有很大优势,但在启动性能方面却存在差距。其原因主要是JavaScript在运行前需要在用户的设备里在线编译,并且每次运行前都需要重新编译;而Native是在打包时PC离线编译,在用户设备中可以直接运行。


简单说Native用的是AOT(Ahead of Time,提前编译)化的语言,因此,我们也尝试将AOT技术应用到JS语言中。

JS AOT的目标是让JavaScript具备动态化特性的同时,运行性能也可能与Native 对标,尤其是首次启动。

技术选型


下图是V8运行JavaScript的流水线。

V8运行JavaScript的流水线


首先V8会使用解析器将JS源代码进行词法分析、语法分析和语义分析,生成抽象语法树(AST),再使用解释器将AST编译生成字节码,然后以字节码的形式解释执行,在解释执行的同时V8会收集JS对象的类型信息,当某些函数多次运行变成热点函数后,V8会使用其优化编译器对代码进行重新编译,结合之前收集的对象类型信息重新生成高效的汇编指令,并以汇编的形式运行,以获得更高的执行性能。

根据V8执行JS的流水线分析来看,实现JS AOT有三种可行方案:本地代码(Machine Code,汇编),全字节码缓存(Full Code Cache),部分字节码缓存(Code Cache)。

1、本地代码(Machine Code,汇编)


早在2010年,V8在执行JS时,先使用解析器将JavaScript源代码解析生成抽象语法树(AST),再使用基线编译器Full-codegen将AST编译成为未优化版的汇编代码后执行;当某些函数多次执行成为热点函数后,再使用优化编译器生成优化版的汇编代码执行。然而,在2016年V8引入了字节码后,就摒弃了基线编译器Full-codegen。


V8 为什么会摒弃基线编译器Full-codegen 呢?这就需要对JS语言规范的标准有一些了解。以加号运算符为例(如下图),在operator+的规范中,会使用到GetValue、ToString、ToNumber、ToPrimitive等语义,同时ToString、ToNumber也会调用ToPrimitive语义,在ToPrimitive语义中会调用GetMethod、Call语义,其中Call语义可能调用任意JS代码,GetMethod会调用GetV获取对象属性,GetV会调用ToObject。由此可见,一个看似简单的加号运算符涉及的标准非常多,而由于JavaScript的弱类型语言特性,在编译时无法确定两个相加的操作数会是数值、字符串、对象等还是任意类型组合,因而需要将这些路径全部编译生成庞大的汇编代码。

通过这个示例可以看到,将JS源代码直接全部编译成未优化版的汇编代码,将面临编译过程缓慢、生成代码体积庞大等问题,进而同时会导致生成的代码占用内存高、因CPU缓存频繁失效导致性能差的问题,另外还存在CPU架构不通用的问题。由此可见,使用汇编代码并不是一个可取的方案,V8验证过这个方案的不可行。


2、全字节码缓存(Full Code Cache)


V8的字节码是一种高度抽象的二进制表示形式,每个字节码都分别对应一段字节码处理程序,每个字节码处理程序由许多条汇编指令组成。V8的解释执行过程是按照字节码顺序依次调用字节码处理程序执行。将JS全部编译成字节码,以字节码的形式运行,可以有效避免运行时的编译过程,但根据线上TOP站点分析发现,它仍然存在一些问题:

  1. JS函数运行覆盖度低(运行函数个数/总函数个数=43%);
  2. 代码膨胀严重(全字节码大小/JS源码大小=2.6倍左右);
  3. 加载 & 反序列化消耗大。


根据对TOP站点的分析发现,对小部分 JS 来说使用全字节码缓存有较明显的性能提升,但在大部分 JS 中相比于直接编译的提升非常微小,基本可以忽略;在实际业务中,将全字节码缓存与部分字节码缓存也进行了性能对比(部分字节码缓存,即只为部分需要被执行的 JS 代码预生成字节码)。该结果表明,相比于部分字节码缓存,全字节码缓存会慢 22.7%。


3、部分字节码缓存(Code Cache)


下图是Blink执行JS的逻辑。


在Blink中执行JS时V8会先只编译最上层的函数,并在运行到内部函数时,如未被编译,则先编译该内部函数后运行。同时JS中会注册DOM事件,在DOM事件被触发时也会执行之前未执行的函数,这时V8也会先将其编译生成字节码再运行,同样如Async function、Promise、Timer等异步任务,也会在遇到未被编译的函数时先编译再运行。最后,当页面跳转或关闭时,将所有编译过的函数序列化并生成字节码缓存。

这个方案存在的问题:

  1. V8(U4内核)版本的碎片化:U4内核对V8深入优化修改时,可能改动到 V8 内部的数据结构,导致不同版本不通用;
  2. 不同CPU架构下的通用性:V8 的字节码是全平台、所有架构一致通用的,但是字节码所使用的部分常量数据(如小整数)的内存布局在不同架构下不一样,导致整个字节码缓存与 CPU 架构相关;
  3. 机型通用性:高、低端机上生成的字节码缓存可能不通用。


根据以上三个方案总结来看,JS AOT面临的主要问题在于生成代码的有效性和兼容性。


方案设计

1、保证AOT的有效性


策略一:追求极致的性能——PGO(Profile-guided optimization)
PGO是基于性能测试结果的优化,JS使用PGO的思路是仅为需要被执行的函数生成代码,整个流程如下图。


在业务发布之前先在移动端访问一次页面,JS执行完后会生成函数运行信息并上报给服务端,服务端结合JS源码对信息进行处理,如补回已被GC的函数、合并和简化信息等,最后在服务端或移动端使用函数信息结合JS源码预编译生成AOT代码。

策略二:追求便捷的使用——先验规则
使用PGO的方式生成AOT,每次发布时都需要先收集函数信息,这在无形中增加了业务发布的复杂度。为了追求更便捷的使用,内置了另一种先验规则的策略。这个策略的核心在于预测需要被执行的函数,然后只为这些函数生成字节码。

下图是一段JS代码,在代码执行前 V8 先编译灰色背景部分的代码,这些代码运行时会编译并执行第二层代码(紫色部分),第二层代码执行时会编译第三层代码(黄色部分),然后是第四层代码(蓝色部分)。



通过对线上TOP站点的JS分析,发现了以下特征:

  1. JS层级嵌套越深,使用率越低;相反,层级越浅使用率越高,如最上层代码的使用率是100%;
  2. JS越小,覆盖度越高:一些小型JS内的函数使用率可以达到90% 以上。


结合以上特征对TOP站点进行不断的实验和分析后,最终确定了一套整体最优的生成策略,如下:

  • 小型 JS:不生成AOT
  • 因为如果生成AOT后都需要从磁盘加载反序列化,可能会比直接编译JS源码更耗时。

  • 中型 JS:全字节码缓存
  • 因为这部分JS函数覆盖度达80% 以上,使用全字节码性价比较高。

  • 大型 JS:只生成Top 3层函数字节码
  • 其它未被编译的函数在运行时用到会及时编译。

  • 运行后增量更新
  • 页面生命周期结束时,将新增编译的函数序列化,增量更新保存;运行次数越多,AOT包含的函数就越多,运行性能越优。


    2、保证AOT的兼容性


    策略一:在线生成

  • 空闲时预热


  • 下图是简化版U4内核线程模型图。首先,在UI线程发起AOT预热,然后在AOT线程将JS预编译生成AOT缓存在磁盘,当用户打开页面,Blink主线程在运行JS代码时加载AOT,避免了JS的编译过程从而提升性能。

  • 影响或不足:存在资源浪费(磁盘 & CPU),部分用户可能不会访问相应的页面;
  • 适用场景:框架JS,不经常变动。

  • 在线生成(空闲时预热)

  • 访问时生成


  • 如下图,当用户打开页面时向后台AOT线程发起预编译任务,让后台线程同步生成AOT,同时Blink主线程发起页面打开加载请求,并进行解析、排版、渲染等操作,在结束这些操作时JS代码也已经在AOT线程编译完成,Blink主线程可以直接加载和反序列化并运行。

  • 影响或不足:如果AOT线程执行缓慢,在Blink主线程执行JS时字节码可能未生成完成,导致Blink主线程需要重新编译;
  • 适用场景:业务JS,已经离线到本地。


  • 在线生成(访问时生成)


    策略二、离线生成

    离线生成的方式提供了一个离线工具,可以在服务端使用函数信息或先验规则结合JS源码预编译生成AOT,然后将AOT内置到发布包中,提供给线上用户直接运行,省去编译的过程。
    适用场景: 

  • APP冷启动时执行(没有预热时机);
  • JS不经常变动;
  • JS不需要动态更新。

  • 影响或不足: 

  • 更新U4内核后,可能需同步更新AOT,如果不更新,可能导致加载失败,需要运行时重新编译。

  • 离线生成

    既然使用了AOT离线生成,那是否可以在线上只保留AOT而抛弃JS源码吗?答案是不可以,原因有以下两点:

  • 抛弃JS源码意味着需要将JS全部编译成字节码,前面提到的PGO或先验规则,都只是生成部分函数的字节码,而有些JS代码是在非主路径才会使用,如果没有JS源码,在调用这些函数时就会出现异常;而使用全字节码缓存则会出现前面提到的问题;
  • JS的一个特殊功能是将任意函数通过toString调用取得原始的字符串,如果将JS源码抛弃,这个功能就无法实现。

  • 3、AOT方案总览


    根据以上介绍的所有内容,AOT整体方案总结如下:
    在预发布环节,首先在移动端访问页面,然后上报函数信息至服务端,服务端进行信息处理后生成函数信息(如果使用先验规则策略则可省去预发布环节);在线上后台线程中使用函数信息或先验规则结合JS源码进行预编译生成AOT,当用户访问页面时可以直接从AOT运行而无需编译,从而提升JS执行性能;对于预热环节,还提供了离线工具,可以在服务端离线生成AOT。


    优化效果


    下面左图是某页面未使用AOT的 Trace结果,右图是基于先验规则打开页面时后台预热生成AOT的结果。对比两个结果发现,后者JS执行时间减少35% 左右。

    另外,针对线上TOP站点进行对比分析发现,使用PGO策略可以使执行性能平均提升49%以上,使用先验规则可以平均提升33.9% 以上。我们以夸克高考为例,对比使用AOT后,首屏性能提升了17.6% 以上。


    展望

    在当前的U4 3.0 & 4.0版本中,V8 执行JS的流程是:通过解析器将JS源码解析生成抽象语法树,然后使用Ignition解释器将抽象语法树编译成字节码并以字节码形式运行;当部分函数多次运行变成热点函数后,V8使用优化编译器将其编译成优化汇编代码并运行。

    而在即将发布的升级U4 5.0版本中,使用V8的火花塞基线编译器,可以从字节码直接编译生成未优化版的汇编代码并运行。当部分JS代码多次运行成为热点函数后,会再次使用优化编译器重新编译,生成优化版的汇编代码。
    新增的火花塞基线编译器,在保留字节码抽象度的同时,将字节码的解码过程提前,并能够充分利用CPU的分支预测能力,从而提升JS的执行性能。初步预测,如果JS AOT使用火花塞编译器后,我们在AOT中可以进行更多的优化,使JS性能提升20% 左右。


    以上。


    关注「Alibaba F2E」微信公众号把握阿里巴巴前端新动向

    马蜂窝数据仓库设计与实践

    Part.1

    马蜂窝数据仓库与数据中台


    最近几年,数据中台概念的热度一直不减。2018 年起,马蜂窝也开始了自己的数据中台探索之路。


    数据中台到底是什么?要不要建?和数据仓库有什么本质的区别?相信很多企业都在关注这些问题。


    我认为数据中台的概念非常接近传统数据仓库+大数据平台的结合体。它是在企业的数据建设经历了数据中心、数据仓库等积累之后,借助平台化的思路,将数据更好地进行整合与统一,以组件化的方式实现灵活的数据加工与应用,以更清晰的数据职能组织应对业务的快速变化,以服务的方式更好地释放数据价值的一种方式。


    所以,数据中台更多的是体现一种管理思路和架构组织上的变革。在这样的思想下,我们结合自身业务特点建设了马蜂窝的数据中台,核心架构如下:



    在中台建设之前,马蜂窝已经建立了自己的大数据平台,并积累了一些通用、组件化的工具,这些可以支撑数据中台的快速搭建。作为中台的另一大核心部分,马蜂窝数据仓库主要承担数据统一化建设的工作,包括统一数据模型,统一指标体系等。下面介绍马蜂窝在数据仓库建设方面的具体实践。



    Part.2

    数据仓库核心架构


    马蜂窝数据仓库遵循标准的三层架构,对数据分层的定位主要采取维度模型设计,不会对数据进行抽象打散处理,更多注重业务过程数据整合。现有数仓主要以离线为主,整体架构如下:


    马蜂窝数据仓库设计与实践


    如图所示,共分为 3 层:业务数据层、公共数据层与应用数据层,每层定位、目标以及建设原则各不相同。


    (1)业务数据层:包含 STG(数据缓冲层)与 ODS(操作数据层)两层,这两层数据结构与业务数据几乎一致。


    • STG:也叫数据准备区,定位是缓存来自 DB 抽取、消息、日志解析落地的临时数据,结构与业务系统保持一致;负责对垃圾数据、不规范数据进行清洗转换;该层只为 ODS 层服务;

    • ODS:操作数据层定位于业务明细数据保留区,负责保留数据接入时点后历史变更数据,数据原则上全量保留。模型设计依据业务表数据变更特性采取拉链、流水表两种形式。


    (2)公共数据层:细分为 DWD(明细数据层)、DWS(汇总数据层)、DIM(公共维度层) 三层,主要用于加工存放整合后的明细业务过程数据,以及经过轻度或重度汇总粒度公共维度指标数据。公共数据层作为仓库核心层,定位于业务视角,提炼出对数据仓库具有共性的数据访问、统计需求,从而构建面向支持应用、提供共享数据访问服务的公共数据。


    • DWD:这一层是整合后的业务过程明细数据,负责各业务场景垂直与水平数据整合、常用公共维度冗余加工,以及明细业务标签信息加工;

    • DWS:汇总数据层按照主题对共性维度指标数据进行轻度、高度聚合;

    • DIM:对维度进行统一标准化定义,实现维度信息共享。


    (3)应用数据层:DWA 层,主要用于各产品或各业务条线个性化的数据加工,例如商业化产品数据、搜索推荐,风控等。



    Part.3

    数据模型设计


    3.1 方法选择


    数据模型是对现实世界数据特征的抽象,数据模型的设计方法就是对数据进行归纳和概括的方法。目前业界主要的模型设计方法论有两种,一是数据仓库之父 Bill Inmon 提出的范式建模方法,又叫 ER 建模,主张站在企业角度自上而下进行数据模型构建;二是 Ralph Kimball 大师倡导的维度建模方法,主张从业务需求出发自下而上构建数据模型。


    大数据环境下,业务系统数据体系庞杂,数据结构多样、变更频繁,并且需要快速响应各种复杂的业务需求,以上两种传统的理论都已无法满足互联网数仓需求。在此背景下,马蜂窝数据仓库采取了「以需求驱动为主、数据驱动为辅」的混合模型设计方式,来根据不同的数据层次选择模型。主要从以下四个方面综合考虑:


    1. 面向主题:采用范式模型理论中的主题划分方法对业务数据进行分类。

    2. 一致性保证:采用维度模型理论中的总线结构思想,建立统一的一致性维度表和一致性事实表来保证一致性。

    3. 数据质量保证:无论范式建模还是维度建模都非常重视数据质量问题,综合使用两个理论中的方法保证数据质量。

    4. 效率保证:合理采取维度退化、变化维、增加冗余等方法,保证数据的计算和查询效率。


    马蜂窝数据仓库设计与实践


    其中,ODS 选择保持贴源的范式模型,不做进一步模型抽象,只是从节省存储角度考虑,对该层采取拉链处理。DWD 与 DWS 基于对构建成本、性能,易用性角度的考虑,主要采取维度模型和一些宽表模型。宽表模型的本质是基于维度模型的扩展,对整个业务以及全节点信息进行垂直与水平方式整合;同时采用退化维度的方式,将不同维度的度量放入数据表的不同列中,实现业务全流程视图的构建,来提升宽表模型的易用性、查询效率,且易于模型的扩展。


    • 水平整合:水平整合就是将同一业务多数据源的数据整合到一个模型中,如果多数据源业务数据存在交集,则需要按照预设的业务规则选取一份保留,避免整合后的业务数据交叉。例如商品数据如果未进行主数据管理,不同业务线的商品信息就会散落在各业务系统表中,无法满足企业级的数据分析需求,这时就需要将这些商品数据按照业务主题进行水平整合。


    • 垂直整合:一次完整的业务流转通常要经历多个环节,各节点信息产生的时点不同、储存的数据表不同。垂直整合就是将同一业务中各关键节点信息整合至业务全流程宽表模型中。马蜂窝订单交易模型的构建就采用了这种方式,下文将进行详细介绍。


    3.2 设计目标


    马蜂窝数据仓库在模型设计上以准确性、易用性、及时性为设计目标,以满足业务人员对数据的多样需求。


    • 准确性:数据质量管控要在建模过程中落地,为数据准确性保驾护航。

    • 易用性:兼顾模型的可扩展性和可理解性。

    • 及时性:充分考虑模型的使用效率,提供方便快捷的数据查询和数据计算服务。


    3.3 设计流程


    马蜂窝数仓模型设计的整体流程涉及需求调研、模型设计、开发测试、模型上线四个主要环节,且规范设计了每个阶段的输出与输入文档。


    马蜂窝数据仓库设计与实践


    1. 需求调研:收集和理解业务方需求,就特定需求的口径达成统一,在对需求中涉及到的业务系统或系统模块所承担的功能进行梳理后进行表字段级分析,并对数据进行验证,确保现有数据能够支持业务需求。

    2. 模型设计:根据需求和业务调研结果对模型进行初步归类,选择合适的主题域进行模型存放;确定主题后进入数据模型的设计阶段,逻辑模型设计过程要考虑总线结构构建、模型规范定义等关键问题;物理模型设计以逻辑模型为基础,兼顾存储性能等因素对逻辑模型做的物理化的过程,是逻辑模型的最终物理实现.物理模型在一般情况下与逻辑模型保持一致,模型设计完成后需要进入评审与 Mapping 设计。

    3. 模型开发:就是对模型计算脚本的代码实现过程,其中包含了数据映射、脚本实现、测试验证等开发过程。单元测试完成后需要通知业务方一起对模型数据进行业务验证,对验证问题做收集,返回验证模型设计的合理性。

    4. 模型上线:完成验证后的模型就可以在线上生产环境进行部署。上线后需要为模型配置监控,及时掌握为业务提供数据服务的状况。我们还将模型的实体和属性说明文档发布给仓库数据的使用者,使模型得到更好地应用。


    3.4 主题分类


    基于对目前各个部门和业务系统的梳理,马蜂窝数据仓库共设计了 4 个大数据域(交易、流量、内容、参与人),细分为 11 个主题:


    马蜂窝数据仓库设计与实践


    以马蜂窝订单交易模型的建设为例,基于业务生产总线的设计是常见的模式,即首先调研订单交易的完整过程,定位过程中的关键节点,确认各节点上发生的核心事实信息。模型是数据的载体,我们要做的就是通过模型(或者说模型体系)归纳生产总线中各个节点发生的事实信息。


    订单生产总线:


    马蜂窝数据仓库设计与实践


    如上图所示,我们需要提炼各节点的核心信息,为了避免遗漏关键信息,一般情况下抽象认为节点的参与人、发生时间、发生事件、发生协议属于节点的核心信息,需要重点获取。以下单节点为例,参与人包括下单用户、服务商家、平台运营人员等;发生时间包括用户的下单时间、商家的确认时间等;发生的事件即用户购买了商品,需要记录围绕这一事件产生的相关信息;发生协议即产生的订单,订单金额、约定内容等都是我们需要记录的协议信息。


    在这样的思路下,总线架构可以在模型中不断添加各个节点的核心信息,使模型支撑的应用范围逐步扩展、趋于完善。因此,对业务流程的理解程度将直接影响产出模型的质量。


    涉及的业务节点越多,业务流程也就越复杂。从数据的角度看,这些业务过程会产生两种基本的场景形态,即数据的拆分和汇聚。随着流程的推进,前一节点的原子业务单位在新节点中可能需要拆分出更多信息,或者参与到新节点的多向流程。同样,也可能发生数据的汇聚。以某个订单为例,下单节点数据是订单粒度的,而到支付节点就发生了数据拆分。数据的拆分、汇聚伴随着总线的各节点,可能会一直发散下去。


    马蜂窝数据仓库设计与实践


    鉴于上述情况,在模型实现过程中,我们不能把各节点不同粒度的数据信息都堆砌在一起,那样会产生大量的冗余信息,也会使模型本身的定位不清晰,影响使用。因此,需要输出不同粒度的模型来满足各类应用需求。例如既会存在订单粒度的数据模型,也会存在分析各个订单在不同时间节点状态信息的数据模型。


    马蜂窝数据仓库设计与实践


    马蜂窝数据仓库设计与实践


    基于维度建模的思路,在模型整合生产总线各节点核心信息之后,会根据这些节点信息进一步扩展常用的分析维度,以减少应用层面频繁关联相关分析维度带来的资源消耗,模型会反范式冗余相关维度信息,以获取应用层的使用便捷。最终建立一个整合旅游、交通、酒店等各业务线与各业务节点信息的马蜂窝全流程订单模型。



    Part.4

    数据仓库工具链建设


    为提升数据生产力,马蜂窝数据仓库建立了一套工具链,来实现采集、研发、管理流程的自动化。现阶段比较重要的有以下三大工具:


    1. 数据同步工具


    同步工具主要解决两个问题:


    • 从源系统同步数据到数据仓库 

    • 将数据仓库的数据同步至其他环境


    下面重点介绍从源系统同步数据到数据仓库。


    马蜂窝的数据同步设计支撑灵活的数据接入方式,可以选择抽取方式以及加工方式。抽取方式主要包括增量抽取或者全量抽取,加工方式面向数据的存储方式,是需要对数据进行拉链式保存,或者以流水日志的方式进行存储。


    接入时,只需要填写数据表信息配置,以及具体的字段配置信息,数据就可以自动接入到数据仓库,形成数仓的 ODS 层数据模型,如下:


    马蜂窝数据仓库设计与实践


    马蜂窝数据仓库设计与实践


    2. 任务调度平台



    我们使用 Airflow 配合自研的任务调度系统,不仅能支持常规的任务调度,还可以支持任务调度系统各类数据重跑,历史补数等需求。


    别小看数据重跑、历史补数,这两项功能是在选择调度工具中重要的参考项。做数据的人都清楚,在实际数据处理过程中会面临诸多的数据口径变化、数据异常等,需要进行数据重跑、刷新、补数等操作。


    我们设计的「一键重跑」功能,可以将相关任务依赖的后置任务全部带出,并支持选择性地删除或虚拟执行任意节点的任务:


    • 如果选择删除,这该任务之后所依赖的任务均不执行

    • 如果选择虚拟执行,则会忽略(空跑)掉该任务,后置的所有依赖任务还是会正常执行。


    如下是基于某一个任务重跑下游所有任务所列出的关系图,选中具体的执行节点,就可以执行忽略或者删除。


    马蜂窝数据仓库设计与实践


    3. 元数据管理工具


    元数据范畴包括技术元数据、业务元数据、管理元数据,在概念上不做过多阐述了。元数据管理在数据建设起着举足轻重的作用,这部分在数仓应用中主要有 2 个点:


    (1)血缘管理


    血缘管理可以追溯数据加工整体链路,解析表的来龙去脉,用于支撑各类场景,如:


    • 支持上游变更对下游影响的分析与调整

    • 监控各节点、各链路任务运行成本,效率

    • 监控数据模型的依赖数量,确认哪些是重点模型


    如下是某一个数据模型中的血缘图,上下游以不同颜色进行呈现:


    马蜂窝数据仓库设计与实践

    (2)数据知识管理


    通过对技术、业务元数据进行清晰、详尽地描述,形成数据知识,给数据人员提供更好的使用向导。我们的数据知识主要包括实体说明与属性说明,具体如下:


    马蜂窝数据仓库设计与实践


    马蜂窝数据仓库设计与实践

    当然,数仓工具链条中还有非常多工具,例如自动化建模工具,数据质量管理工具,数据开发工具等,都已经得到了很好地实现。



    Part.5

    数仓应用——指标平台


    有了合理的数仓架构、工具链条支撑数据研发,接下来,就要考虑如何把产出的数据对外赋能。下面以马蜂窝数据应用利器-指标平台,进行简单介绍。


    几乎所有的企业都会构建自己的指标平台,每个企业建立的标准都不一样。在这个过程中会遇到指标繁多、定义不清楚、查询缓慢等问题。为尽量避免这些问题,指标平台在设计时需要遵循几大原则:


    1. 指标定义标准,清晰,容易理解,且不存在二义性,分类明确

    2. 指标生产过程简单、透明、可配置化

    3. 指标查询效率需要满足快速响应

    4. 指标权限管理灵活可控


    基于以上原则,马蜂窝的指标平台按照精细化的设计进行打造,指标平台组成架构如下图:


    马蜂窝数据仓库设计与实践


    其中:


    1. 数据仓库是指标数据的来源,所有指标目前都是通过数据仓库统一加工的

    2. 指标管理包括指标创建与指标元数据管理:数仓负责生产并创建最核心、最基础的指标;其他人员可以基于这些指标,按照规则进行指标的派生;元数据管理记录指标的具体来源路径,说明指标的数据来源是数仓表,或者是 Kylin,MySQL 或 ES

    3. 指标字典对外呈现指标的定义、口径、说明等,保证指标的透明化及可解释性

    4. 数据服务接受指标的查询请求,针对不同场景判断查询的成本,选择最优链路进行指标查询,并返回指标查询的结果

    5. 多维查询将可以提供查询服务的指标与维度通过界面呈现,用户可以基于维度选择指标或基于指标选择维度,查询具体需要的数据

    6. 权限管理贯彻始终,可以支持表级、指标级、维值级别的权限管理



    Part.6

    总结


    企业的数据建设需要经历几个大的步骤:


    • 第一步,业务数据化:顾名思义,一切业务都能通过数据反映,主要指的是将传统线下流程线上化;

    • 第二步,数据智能化:光有数据还不行,还需要足够的智能,如何通过智能化的数据支撑运营、营销及各类业务,这是数据中台当前解决的主要问题;

    • 第三步,数据业务化:也就是我们常说的数据驱动业务,数据不能只是数据,数据价值最大化在于可以驱动新的业务创新,带动企业增长。


    目前大部企业目前都停留在第二个阶段,因为这一步需要足够夯实,才能为第三步打好基础,这也是为什么各大企业要投入很大成本到大数据平台、数据仓库乃至数据中台的建设中。


    马蜂窝数据中台的建设才刚刚起步。我们认为,理想的数据中台需要具备数据标准化、工具组件化、组织清晰化这三个核心前提。为了向这一目标迈进,我们将建立统一、标准化的数据仓库作为当下数据中台的重点工作之一。


    数据来源于业务,最终也将应用于业务。只有对数据足够重视,与业务充分衔接,才能实现数据价值的最大化。在马蜂窝,从管理层,到公司研发、产品、运营、销售等各角色,对数据非常重视,数据产品的使用人数占公司员工比例高达 75%。


    大量用户的使用,驱动着我们在数据中台建设的路上不断前进。如何将新兴技术能力应用到数据仓库的建设,如何以有限的成本高效解决企业在数据建设中面临的问题,将是马蜂窝数仓建设一直的思考。



    (题图来源:网络)



    新福利:


    猜你喜欢

    1、

    2、

    3、

    4、


    以上是关于V8 JS AOT化的探索与实践的主要内容,如果未能解决你的问题,请参考以下文章

    Angular2 AOT 编译与静态外部 js 文件

    nodejs01

    Node.js 入门简介

    NodeJS基础

    V8 执行 JavaScript 的流程

    基于DevOps 微服务以及k8s的高可用架构探索与实现