覆盖数万研发人员,字节跳动首次公开效能度量核心技术!
作者 | 姜磊
审校 | 蔡芳芳
引言
本文将详细介绍字节跳动研发效能度量平台 DevMind 的建设过程、架构设计以及业务实践案例。希望能以 DevMind 为例,为企业内部的数字化场景提供具有参考价值的分析思路和解决方案。全文分为七个章节,分别是困境与破局、挑战与原则、架构设计、平台层技术要点、数据模型层技术要点、应用层技术要点和未来展望。另外对字节跳动研发效能度量体系和实践感兴趣的读者朋友,请移步:字节跳动DevMind体系与最佳实践
第 1 章:困境与破局。 首先,回顾 DevMind 的建设背景,这里包括对市场行业大环境的分析,以及字节跳动内部各层次需求的介绍。其次,对“研发效能”这一主题进行深度剖析,从研发效能发展历程入手,介绍当前业界在研发效能度量领域的探索和工程实践。最后,重点介绍 DevMind 在研发效能提升方面推广运营的经验。第 2 章:挑战与原则。 首先,介绍在 DevMind 建设过程中的五大挑战,包括数据可视化表达能力、全域数仓建设以及研发人力压力等。其次,结合上一章的业务需求分析结论,以及第一性原理的思考方式,推导出 DevMind 平台在业务、产品和技术架构上需要一以贯之的原则,即“DevMind 平台建设三原则”。第 3 章:架构设计。 在明确了 DevMind 平台建设三原则之后,在此基础上推演整个平台的架构设计方向。架构的构建分为横纵两个视角:横向视角表示数据从采集到消费的完整数据生命周期;纵向视角表示从数据可视化到数据运营的业务消费层次。第 4 章:平台层技术要点。 介绍 DevMind 核心技术组件——即席查询引擎,以及在此之上实现的 DSL 解析优化器和 UDF 框架。此外,在数据可视化分析领域,仅仅告诉用户“What”还远远不够,还需要在此基础之上告诉用户“Why”。因此,本章会简要介绍 DevMind 在一般性时间序列指标上的归因能力探索。在本章最后部分,指出在研发效能度量领域长期被有意忽视的问题——数据安全,以及 DevMind 基于隐私计算相关能力,在数据安全方面的多种努力。第 5 章:数据模型层技术要点。 本章不会囿于维度建模、数仓分层等通识性内容的介绍,而是重点剖析当前数据可视化分析类产品设计过程中因专业性的僭妄,与普通用户效率之间撕裂的鸿沟。本章重点介绍 DevMind 对此类问题的思考,以及产品化的解决方案。第 6 章:应用层技术要点。 本章将以两个应用研发效能度量的典型场景为例,介绍 DevMind 相关的解决方案及应用实践。这些方案能够很好地展现 DevMind 如何通过对数据分析全链路的掌控,降低终端用户使用成本。第 7 章:未来展望。 本章首先介绍了 DevMind 在技术选型、技术方向的分析思路,然后提出了由技术驱动的 DevMind 后续发展三驾马车——查询引擎、数据科学、图分析。名词解释:
SDLM:Software Development Lifecycle Management,软件研发生命周期管理,由 Gartner 于 2011 年提出。
ADLM:Application Development Lifcycle Measurement,应用研发全域度量,由 Gartner 于 2019 年提出。
BI: Business Intelligence,商业智能,用于将企业中的现有数据进行有效整合,快速准确地形成报表并作为决策依据,帮助企业管理者做出明智的业务经营决策。
ABI: Analytics and Business Intelligence Platform,分析和商业智能平台。
Augmented Analytics:增强分析,由 Gartner 于 2017 年提出。
DAG: Directed Acyclic Graph,有向无环图。
ERP: Enterprise Resource Planning,企业资源计划。
Citizen Data Scientist:公民数据科学家。指未正式接受过高级数学和统计学培训的知识工作者,通过使用应用程序从数据中提取高价值见解。
DORA: DevOps Research and Assessments。DORA 是 DevOps 领域最权威的行业研究组织之一,于 2014 年成立,并于 2018 年被 Google 收购。从 2014 年起,DORA 每年会产出一份关于整个 DevOps 行业的报告。业界最通行的 DevOps 交付能力四大指标,即是由 DORA 设计并推广的。
TEE: Trusted Execution Environment,可信执行环境。基于硬件的安全机制,将参与计算的代码和数据加载至一个受 CPU 保护的可信环境中,以获得在机密性和完整性上的保护。相比于操作系统,TEE 提供了更高级别的安全性,因此适合处理敏感数据。
第 1 章 困境与破局
1.1 建设背景
字节跳动研发效能度量团队成立于 2020 年 6 月。回望当时的时间节点,再回到字节跳动内部,直到 2020 年年初仍然没有一款面向全公司的、服务于研发全周期的数字化产品。研发链路的相关数据散落在各个研发工具内部,各条业务线都有一个或多个类似的定制化看板产品,只用于度量各自业务的研发效率和质量。因为缺乏公司级的数仓建设,不同业务线间的研发效能情况无法横向比较,甚至在一个业务线内的不同部门,对同一个指标都有着不同的口径定义。因此,在公司内部对研发效能度量有三大诉求:首先,完成整个软件研发生命周期数据数仓的统一建设;其次,梳理指标口径定义,确保业务线内部可以统一“北极星指标”,并在公司层面大业务线间实现横向比较;最后,基于度量发现问题并提出解决方案,推动业务方优化。
1.2 软件研发效能度量及其难点
自 1968 年软件工程的概念被正式提出以来,追求更高的工程效率和更好的交付质量一直是软件工程师的奋斗目标。在此后的近 60 年时间里,关于如何提升软件研发效能及研发效能度量,有着诸多理论探讨和实践经验。比较著名的理论实践有 1981 年《软件工程经济学》一书中提出的 COCOMO 概念、1995 年发表的 Scrum 方法、2001 年雪鸟会议发布的《敏捷宣言》中提出的 Agile 理念,以及 2009 年在 Velocity 大会论文中确立的 DevOps 模式。在最近十年中,研发效能度量领域最知名的理论是 Gartner 于 2011 年提出的 SDLM 分析框架。SDLM 的全称为 Software Development Lifecycle Management,即软件研发生命周期管理,其核心思想是从软件需求分析到发布交付阶段的全流程统计和管理。
在这些理论的指导下,软件研发过程逐渐向大规模工业化生产流程演化。但是对于研发效能度量,业界却鲜有最佳实践出现,甚至对软件研发过程能否准确度量,业界仍有不少争议。
软件研发效能度量的第一大难点在于“软件研发效能”这一研究课题本身。“软件研发效能”涉及软件研发链路中的每个环节,同时包含现代企业内部管理协作模式和业务收益评估等多个课题。在这一母题之下,每个环节都有其独有的领域知识门槛,同时各个环节之间又有着千丝万缕的联系。在度量的过程中稍有不慎,就会令业务囿于局部优化的陷阱之中。
由于每位软件研发工程师都参与整个软件研发流程,因此很容易因熟悉而导致轻视流程的复杂性。很多公司将研发效能度量工作交付给开发者工具团队,或者交由质量保障团队兼任。这种工作部署思路很容易理解,因为这些团队本身就是数据的拥有者,因此让他们进一步消费数据也在情理之中。这些团队能迅速完成从零到一的研发效能度量建设工作,但在全局度量的建设中,由于知识局限性、路径依赖或部门墙等因素的影响,而显得后劲不足。
软件研发效能度量的第二大难点在于开发流程的灵活性。现代工业化生产链路虽然复杂,但是在确认生产模式后并不会频繁变更,因此 ERP 可以定制化构建生产过程的度量系统。软件开发过程主要依赖团队间不同角色的分工协作,在这种多方参与的脑力劳动中,很容易出现复杂的环节依赖,而这些流程依赖又极具多样性和易变性。字节跳动研发团队需求流程管理都是通过字节自研的项目管理工具进行的。字节项目管理工具的一大产品特色是能够使管理员用户低成本的完成需求节点流程模板的配置,因此各业务研发团队都会基于自身特点配置不同的需求流程模板。以抖音某子方向百人研发业务团队为例,该团队当前同时使用的需求流程模板数量达到 5 个,在一年中使用过但后续废弃的需求流程模板数量超过 20 个。需求节点流程的低成本创建,有利于业务研发团队基于自身需要快速更新流程。但是因为已经完成的需求流程的流转信息仍然记录在旧模板中,所以在研发流程度量场景中,需要挖掘过去与现在的数据信息,并保持前向和后向兼容性。
软件研发效能度量的第三大难点是其交付结果投入高、收益计算复杂。软件最终交付件是完全非标准化的,且通常不会重复完成的项目。即使有 A/B 实验的加持,其数学原理也只能解释单一需求的指标组变化,难以准确计算指定团队整体的业务收益。在对团队或者个体进行度量时,度量对象规模越小,则指标可靠性越会迅速劣化。
1.3 转动效能提升的飞轮
世界上的游戏可以大致分为两种:一种称为有限的游戏,另一种则称为无限的游戏。有限的游戏追求输赢,并以取胜为目的。无限的游戏则以存续游戏为目的,永远在探索边界。无限的游戏中可以包含无数个有限的游戏。当人们意识到自己在玩的是一场无限的游戏时,心态就会从单纯追求单场战役的 winning-seeking 转变为 for a long live。
研发效能提升是每一个研发团队所追求的,但实际情况是,当某一效能问题成长为“屋里的灰犀牛”后,它才被人们真正重视。在常见的研发效能提升活动中,研发团队通常会以某只“灰犀牛”为目标——团队驱动自身进行流程和方案的改进,进而打赢这场有限的游戏。人们都知道,研发效能提升是一场持续精进的“修行”,却只能用一场场有限的游戏去对抗熵增。就像拉姆斯菲尔德在“未知的未知”言论里提到的那样,研发效能这一母题本身幽暗而深邃,限制效能的瓶颈、提升效能的方法都隐匿在未知的未知之中。字节跳动的研发团队希望能够通过转动效能提升的飞轮,让研发效能提升成为一场无限的游戏。在这个过程中,字节跳动转动了两个飞轮:协作的飞轮和价值的飞轮。
1.3.1 协作的飞轮
在效能提升这场游戏中,第一位玩家通常是业务团队,他们往往带着问题闯入研发效能度量的游戏。在问题驱动改进的过程中,团队需要定量指标来验证自身的改进效果。当越来越多的团队着手于类似的改进过程,或者高层管理者意识到需要推动效能提升之后,第二位玩家——研发效能平台就参与进来了。这也是“在互联网企业发展过程中,会首先出现散落在各团队间的自制效能大盘,然后演化出一个公司中台性质的研发效能度量平台”的原因。
正如前文所提,在研发效能这一母题下,各个领域有着很高的领域门槛。因此,特大型互联网企业会引入第三位玩家——研发效能专家。此时,三位玩家还只是在玩一场由目标或问题驱动的有限游戏,专家带着问题在度量平台中进行数据分析,将分析结论反馈给业务方,业务方再采取后续改进动作。在这一个个的专项项目中,业务方、专家、平台的工作都显得非常充实,但这仍然只是散落着的有限的游戏。
为此,在这场游戏中,字节跳动构建一个令三位玩家高效协作的生态模式。首先是在业务方和效能专家之间,业务方对效能专家提出其具体效能提升的诉求,而效能专家基于经验和分析给出专项解决方案,并在业务方改进过程中持续参与,以此帮助业务方落地并打磨自身方案;其次是效能专家和度量平台,效能专家在平台中进行业务全域数据的洞察分析,而平台在效能改进方案落地后,将成功案例沉淀到平台的专家系统中;最后是业务方和度量平台,业务方将度量平台作为管理运营监控的工具,以衡量改进效果,度量平台通过业务方的使用不断优化业务指标口径精度。在协作的飞轮转动的过程中,业务方可以获得研发效能的提升,效能专家可以打磨其效能提升方法模型,度量平台在对功能的迭代过程中使平台能力更加完备。
图 1-1
1.3.2 价值的飞轮
第二个飞轮是价值的飞轮。度量本身并不创造价值,只有洞察后的效能提升才真正为业务创造长期价值。“猎杀屋里的灰犀牛”的价值是不言而喻的,但更重要的是以长期业务价值为北极星,转动研发效能提升价值的飞轮。在这个飞轮中,有两大摩擦力:第一个是业务研发团队的配合参与度,也就是论证长期业务价值和短期资源投入,以及研发习惯重塑之间的 ROI;第二个是如何高效搜寻更多的灰犀牛,最好是做到“除之于未有型”。字节跳动以研发效能度量团队作为效能提升飞轮的发动机,由供给侧驱动飞轮的高速运转。
作为供给侧动力源的研发效能度量团队,主要有三个方面的工作:做数据,使 ADLM 研发效能数仓领域完备、数据置信;做产品,通过更丰富、易用的数据可视化分析工具,辅助数据分析师高效地完成数据分析工作;做算法,通过不断构建并打磨底层算法库,使描述型分析和诊断型分析完全自动化,并在此之上构建更高维度的分析能力。
紧接着,研发效能专家可以基于完备的数据和易用的分析工具,高效地产出富有洞见的研发效能分析报告。真正有价值的报告势必会打动业务方,其分析结论也将被业务方采纳。
业务方基于分析报告和专家的 BP 来改进研发流程、方法、工具。改进将带来研发效能的提升,而研发效能提升又带来了业务价值的长期增长。
业务价值的增长将激励业务方进行更深度的效能改进,同时单个业务方的成功也会吸引更多团队参与到这场游戏之中。
每个研发团队有着各自的诉求和困惑,这些痛点会让更多人对研发效能度量感兴趣,因而吸引更多的领域专家或者效能提升的关注者加入这个领域。
分析师的加入又会推动研发效能度量平台不断完善自己的三项工作,最典型的就是数据准确性的打磨。正如前文所提到的,软件研发具有极强的易变性,因此,数据如果不能被持续地使用、校验,那么就将迅速“腐烂”,直到完全不可使用。
价值的飞轮真正开始转动起来之后,将吸引更多的分析师、研发团队、业务项目。在飞轮的高速旋转中,局部的优化会迅速扩散到整体,最终提升整个组织或企业的研发效能。
图 1-2
第 2 章 挑战与原则
研发效能母题的庞杂深邃,因而在 DevMind 的产品设计过程中,我们怀有足够的敬畏之心。也正是因为这份克制,DevMind 本身并没有做过多的业务假设和定制化开发。当我们抛开研发效能业务本身去关注 DevMind 内核时,可以发现它更接近于一个通用的数字解决方案。DevMind 平台背后的产品理念和技术架构,完全可以低成本地转换为任意一个垂类业务场景的数字化解决方案。
2.1 五大挑战
为了保证研发效能提升的飞轮持续转动,达到超越业务的技术目标,DevMind 在技术方面面临着以下五大挑战。
1.数字可视化表达能力的突破
BI 及其衍生的数字可视化分析工具自 20 世纪 80 年代被提出,至今已逾 40 年。但产品交互的内核却没有发生重大改变,本质仍然是用可视化交互的形式组装 SQL 语言。依赖于 SQL 的产品设计存在两个重要的缺陷:第一个是 SQL 本身的表达能力存在局限性,比如 JOIN 语句的交互式表达,以及对查询结果的再处理能力;第二个是产品使用的隐性门槛高,再精妙的交互设计也无法完全避免用户要事先理解 SQL 的表达逻辑。
2.自动洞察算法的丰富
自动洞察算法按照难易程度可以分为四个层次:第一个层次是 What——描述型分析,用于客观描述和评价目标现状;第二个层次是 Why——诊断型分析,是指对现状(如异常波动)进行诊断定位,直接给出具体原因;第三个层次是 How——建议型分析,是指在理解客观现状、定位问题原因之后给出解决方案;第四个层次是 What if——推演型分析,基于历史数据和算法假定引入某个主动型变化后,对业务可能产生的定量影响。
3.有限的研发人力和旺盛的需求之间的矛盾
相比于 DevMind 已有的业务体量,其研发团队的体量始终保持在一个非常小的范围之内。当飞轮开始转动时,如何用有限的人力承接奔涌而至的业务需求?这要求平台具备前瞻性的架构设计,从而为未来演进预留空间。同时,系统内部遵循着高内聚、低耦合的系统分层,以便保证功能迭代的效率。
4.全域数仓建设
研发效能业务涉及多个领域,这些领域数据首次被提炼为标准数仓的过程耗费了高昂的人力成本。此外,因为研发活动本身具有易变性,因此数仓在初次建立完成后,其日常治理和维护仍然需要不菲的人力开销。
5.指标口径的消歧
指标口径的歧义来源于两个方面:一是人们对业务理解的不同,例如在计算“线下问题拦截率”时,灰度阶段发现的指标是否应记入分子;二是对业务线度量对象的描述不同,在字节跳动这个扁平化、中台化的组织架构中,无法准确地描述一个业务团队的工作范围。
2.2 三项原则
新产品从脑暴阶段到真正设计阶段的关键在于目标的收敛和原则的确立。在产品建设之初就约定设计原则,有三个方面的好处:首先是对齐目标,聚焦团队的核心关注点;其次是迫使设计者的思考有足够的深度,避免用战术上的勤奋掩盖战略上的懒惰;最后是为产品和研发的后续设计施加约束条件、提高沟通效率,便于整体架构的设计和规划。
在 DevMind 平台建设之初,确立了产品建设的三大原则及相应的关键步骤。这三大原则分别是:以 ADLM 为业务目标,以数据中台为整体技术架构,以 ABI 为产品内核。对应的三个关键步骤分别是:构建在线的 ADLM 全域数仓,关注全局、关注结果,警惕局部优化的陷阱;通过指标中台和业务映射的方式实现对数据资产的沉淀;消除指标口径的歧义。
图 2-1
2.2.1 以 ADLM 为业务目标
ADLM(应用研发全域度量)是 Gartner 于 2019 年提出的概念。ADLM 与业界常用的 SDLM 分析框架之间的显著区别是将首字母由 S(Software)替换为 A(Application)。相比于 Software,Application 有两个显著的特点:一是 Application 需要持续迭代、持续交付;二是 Application 始终在线 运行,所以需要对其状态进行持续跟踪和优化。因此,ADLM 理念倡导在 SDL 软件研发流程闭环的基础上,完成“项目-开发-发布-效果/品质”的全流程闭环管理。
以终为始,只有对需求的最终线上效果、App 整体品质的持续度量跟踪,才能倒逼研发流程的度量和改善。例如:对于 ToC 产品,公司会考虑通过 A/B 实验结果、客户端监控、服务端 SLA 等衡量业务收益;对于 ToB 产品,则使用 SaaS 续费率、增购率等作为衡量指标。因此,DevMind 在建设初期就将 App 线上品质、需求收益、用户反馈等结果指标置于非常重要的位置。而这背后也有着诸多的挑战,例如对字节内 App 矩阵上报的品质数据进行清洗和计算,对需求收益的准确归类,等等。
ADLM 在整体上可以分为 CollabChain、SDLM、Online 三大业务域。CollabChain 包括项目协作、需求交付;SDLM 包括代码开发、发布变更、质量检测、安全合规;Online 包括效果品质和线上运营。
除了三大业务域,ADLM 还包含四类度量对象,分别是业务线(树形)、产品(散点型)、部门/汇报线(树形)、个人(单点型)。
2.2.2 以数据中台为整体技术架构
2012 年,陆兆禧担任阿里巴巴首席数据官,随后开启了阿里轰轰烈烈的数据中台建设之路。如今,数据中台几乎已成为各个互联网公司的标准配置。这些公司级的数据中台实际都缺失一个定语——「业务」数据中台,其中存储的是公司业务、经营类数据。实际上,业界鲜有提及「内部」数据中台。原因在于,「内部」数据中台建设的收益不易衡量,且建设的道路幽暗又漫长。
研发效能度量 ADLM 场景链路复杂,流程高度分化,涉及诸多数据源平台。对数据源的首次提炼和后续维护都有着巨大的工作量。同时,由于研发活动本身的易变性和多样性,为维度设计、指标定义等带来诸多挑战。综合考量上述困境,数据中台模式无疑是最佳的解决方案。
目前,DevMind 团队顺利完成了「研发」数据中台的初步建设,其中有两个决定性的因素。一是,当研发效能提升飞轮转动起来之后,其所带来的价值已经完全能够证明数据中台模式的正确性;二是,DevMind 团队成员都是一群坚持长期主义的“笨蛋”,他们不在乎初期的个人收益和绩效产出。
2.2.3 以 ABI 为产品内核
研发效能横跨多个领域,不同业务和不同层次的用户有着各自不同的诉求。DevMind 无法以有限的人力支撑定制化开发模式,以满足所有用户的需求。因此,在这场研发效能提升的无限游戏中,DevMind 不如后退一步,把更多的舞台交给分析师和领域专家。就像一场华美的舞剧,业务方如同观众,负责欣赏舞台之上分析者的智慧和洞见;而 DevMind 负责搭建这个舞台,让分析者能发挥出最好的水平。
正因为如此,DevMind 将 ABI,也就分析和商业智能平台作为产品底层内核,生产者用户和消费者用户有着同等重要的位置。DevMind 在不断增强数据可视化表达能力和分析能力。在数据准备阶段,DevMind 已建设好 ADLM 全域数仓,并且提供自动化建模能力;在洞见分析阶段,DevMind 提供超越交互式 SQL 的交互模式,同时通过自动洞察帮助用户发现数据中的规律和变化;在结果展示阶段,DevMind 利用自动化报表等技术降低数据消费的门槛。
值得指出的是,DevMind 是以 ABI 为产品底层内核,但是将通过产品、交互、技术等多个层面的努力构建公民数据科学家友好型的产品设计。公民数据科学家是指未受过高级数学和统计学正式培训的知识工作者,他们能够通过适配的应用产品从数据中提取高价值见解。DevMind 通过降低使用门槛,充分释放每一位分析人员的潜力。进而通过分析结论供给侧的繁荣,不断发掘研发效能链路的优化方向,最终加速转动效能提升飞轮。
第 3 章 架构设计
DevMind 整体技术架构按照业务目标可划分为“一横一竖”两大方向。
横向架构源自数据生命周期,用数据中台模式构建整个底层基座,其中包含数据采集、数据定义、数据消费。
纵向架构是 DevMind 产品本身的技术架构,可以分为两层,上层是直接面向终端用户的平台能力,下层是“乐高化”的基建模块。
图 3-1
3.1 横向数据生命周期
从数据分析的视角来看,数据的一生会经历三个阶段:数据采集、数据定义和数据消费。
3.1.1 数据采集 ETL
DevMind 从不生产数据,只是数据的搬运工。所有的数据都是用户在源平台中活动时留下的痕迹。DevMind 需要逐个平台进行对接,这需要大量沟通协调的工作。例如,字节跳动内部有近十个 OnCall 反馈类平台,DevMind 需要耗费大量精力分别和各 OnCall 平台沟通数据接入,并实现数据维度对齐。此外各个源平台性质不同,采集方式也有较大差异。例如:对于客户端监控数据,其每日上报的原始数据可达 100+TB,因此需要基于 Flink 进行流式写入;而对于配置类平台,如果不提供相应的 Hive 数仓或 OpenAPI,就需要监听、捕获、更新数据。数据采集 ETL 的过程不仅仅是将原始数据写入 ODS 表,还需要深刻理解业务背景和数据源特性,完成从 ODS 表到 DW 表的转录。
3.1.2 数据定义
数据定义又可以分成数据建模和指标定义。
数据建模是指基于对业务数据的理解和数据分析的需要,将各类数据进行整合和关联,让使用者能够快速、高效地获取数据中有价值的信息。数据建模需要从业务逻辑和数据逻辑两方面考虑,因此要求建模人员同时具备深刻的业务理解和大数据知识储备。此外,数据模型的复杂性会直接影响数据分析的效率。当前,DevMind 广泛使用的数据模型有基于维度建模的雪花模型,以及笛卡尔积模型和 Cube 模型。
指标定义基于指标中台体现,指标中台的核心在于元信息数据模型设计,对其最重要的要求是:强悍且可扩展的数据表达能力。为了实现这个要求,需要对数据模型进行分层解耦的模块化设计,并保证每层模型均是结构化的。
业务模型是对物质世界的一种抽象,指标中台的数据模型是对底层存储引擎的一种抽象。目前公司主流的存储引擎有:基于 SQL 查询的关系数据库(如 MySQL、ClickHouse)、图数据库,以及通过 OpenAPI 开放的各类数据平台(如 DataRocks、Metrics、VCloud)。
对存储引擎的查询方式可以按照 MECE 分析法进行拆解,例如:图数据库可以拆成点和边;SQL 可拆解成四要素——数据源、指标、特征、维度;各 OpenAPI 也可以进行相应的拆解。所以,指标平台数据模型第一层是基础信息层(Basic Module)。
在基础信息层之上是元指标层(Meta-Metric Module)。元指标在物理意义上是指可被存储引擎执行的业务单元,例如关系数据库中的 SQL 语言、图数据库中的 Gremlin 语言。元指标与存储引擎交互后获得代数结果。业界指标中台元信息的记录通常止步于基础信息层相关数据的元信息。但是,指标中台最终是为上层的数据分析服务的。而随着分析的深入,数据分析师们会引入愈发复杂的数据模型和算法。设计元指标层的目标就是有效地管理这些复杂模型和算法的元信息。
图 3-2
DevMind 中最重要的领域知识资产,是沉淀在指标中台中的指标定义。设立研发效能指标中台的首要目的是回答用户最为关心的三个问题,即:有哪些数据?数据质量如何?如何使用数据?
业界推广指标平台/指标字典的第一推力就是回答“有哪些数据”这个问题,其背后隐含了三个子问题:是否有用户关心主题域的数据源?主题域内有哪些指标?指标下的具体业务口径和血缘关系?因此。在指标接入过程中,指标中台需要负责规范化指标命名、规范化统计口径、规范化指标等级。数据质量是指标公信力的基础,指标平台必须明确回答用户取数过程中的基本问题。这里所说的数据“质量”的范畴会比较广,包含指标本身对目标问题的刻画能力、指标背后数据源维度全面性、数据源例行的产出任务 SLA。“如何使用数据”指的是用户在明确数据源、指标口径后如何方便快捷地提取数据。对于这个问题,浅层的应用是做指标字典,为用户取数提供指引和说明,但本身不具备取数能力;高层次方案是提供 OneService 数据服务,通过明确的指标 ID 和规范化的接口定义屏蔽底层存储引擎,提高数据接入效率。此外,值得注意的是,“如何使用数据”这个问题的字面含义有时往往被人们忽略。大多数指标口径的定义和计算逻辑通常比较简单,所以人们不会认为“如何使用数据”是一个问题。实际上,随着业务发展深入,需要更复杂、更抽象的指标去刻画更深层次的问题,这也是指标平台亟需提供的能力。3.1.3 数据消费
此处数据消费不仅指数据分析,还包括数据运营。在数据运营活动中,首先会明确目标,并以 SMART 原则构建目标的北极星指标。围绕北极星指标,再构建相应的数据指标体系,并以定量化的方式指导和推动业务落地。例如,DevMind-Insight 模块不仅是在线化分析报告,还涵盖了评论、任务录入、问题反馈等管理协作功能。
3.2 纵向产品功能分层
图 3-3
DevMind 在产品功能上分为 Insight、Measure、Platform、Nudge 四大模块。其中,Platform 承担数据生命周期中指标/数据定义的任务,Measure、Insight、Nudge 分别对应数据生命周期中数据消费的描述、分析、行动三个步骤。在技术上。四个模块的具体功能是平台能力层原子能力的封装展现,例如「指标」这一元信息会串联起所有模块。
Insight(洞察):用于研发管理辅助,以报告为中心输出洞察内容。由专家基于指标完成主题的聚合,将内容“汇报”给业务方,并让用户基于报告管理团队、跟踪改进任务。Measure(度量):用于数据可视化分析和监控大盘的搭建,以丰富的数据可视化表达能力帮助专家对数据进行深入探查和分析。Platform(指标中台):用于数据资产的建设和沉淀,进而降低生产者用户和消费者用户管数、取数、用数的门槛。Nudge(助推):用于研发过程辅助,在研发过程中提示风险和问题,并辅助解决,消除研发过程中等待和浪费时间,帮助一线研发人员更好、更顺畅地完成工作。近 10 年来,中台化设计被业界广泛讨论。从阿里巴巴力推的“大中台,小前台”,到此前张勇表述的“现在阿里的业务发展太慢,要把中台变薄,变得敏捷和快速”。这种态度的转变有其必然性,尤其是当一个中台业务不断扩大自身业务面时,它就越容易受到对业务支持不足的批评。因为随着业务面的扩大,中台业务会面对更多的业务定制化长尾需求。
上述讨论会引出一个问题:如果中台是一个伪命题,那么架构是否要回到“由业务掌控的敏捷迭代”这个历史的窠臼之中呢?答案显然是:NO。如同军事上的师改旅,更适合中台化的方式可能是从大中台转为厚中台。位于 DevMind 产品下层的乐高化基建层正是贯彻了厚中台的思想,按照“高内聚、低耦合”的原则设计平台能力。各功能模块既能独立提供服务,又能通过有效组合满足更为复杂的产品需求。例如,异动报警能力是指通过统一调度模块触发规则引擎查询底层数据,当命中规则后,通过消息投递服务通知用户。
DevMind 乐高化基建层分为六大功能模块:指标元信息、即席查询引擎、洞察分析算法库、任务调度、模板生成器、消息投送。
指标元信息:元信息模块是所有沉淀数据资产的外化体现,是数据生命周期中数据中台的核心,贯穿平台产品每一个角落,堪称“迷走神经”。模块包含全部业务元信息,比如指标、维度、数据源等,并以模块化建模的方式保证各部分的正交性。即席查询引擎:引擎作为平台技术基座,负责底层异构存储的数据查询,并提供代数模型计算、树形结构查询等多种复杂能力。引擎是平台数据可视化表达能力的核心载体。洞察分析算法库:ABI 类平台的核心竞争力就在于首字母 A——Analytic,也就是分析算法库的深度。当前平台已经完成算法库一期建设,支持多种算法能力,包括趋势描述、异动检测、波动分析(数值型指标、除法型指标)等。任务调度:与业务逻辑解耦的通用化任务调度模块,支持定时触发与任务编排,同时具备算力或时效 Quota 控制能力。当前已支持预刷、报警、报告推送等多种场景。模板生成器:主要用于 Dashboard、报告等场景的模板转换和实例生成等场景。模板生成器是将用户知识沉淀到平台的主要路径,同时可能是全平台业务逻辑最复杂的模块。因此,其采用分层化设计,每个层次负责对应的业务目标,实现高内聚、低耦合。消息投送:消息投送模块是运营环节必不可少的主动触达通路,其将结合 Lark 本身丰富的富文本表达能力,为用户带来良好的体验和及时的信息反馈。第 4 章 平台层技术要点
4.1 即席查询引擎
在正式讨论即席查询引擎之前,先引出一个问题:研发效能度量的业务目标能否通过“自建 ADLM 全域数仓+业界成熟的大数据可视化解决方案”来完成,如 PowerBI、Tableau 等?
应用软件研发效能业务具有极高的复杂性,要求产品具备强大的数据可视化表达能力。研发效能度量领域最经典的图表是“需求累积流图”,它可能就已经是传统的 SQL 型 BI 平台表达能力的极限。而除此之外还有“历史需求当期库存图”、“自然时间下 30 天内未修复缺陷图”等等更为复杂的图表。这样的数据可视化表达能力是 SQL 型的 BI 平台所不具备的。因此,DevMind 决定自建即席查询引擎,并将其作为整体数字化解决方案中最关键的一环。
在设计之初,DevMind 已经意识到,在对即席查询引擎的需求中,唯一不变的就是变化本身。设计过程中会遇到表达能力的挑战、性能的挑战、数据一致性的挑战,以及业务定制化需求腐蚀的挑战。因此在设计时,遵循高内聚、低耦合的中台化分层架构,同时通过 Middleware、UDF 等手段将定制化需求的熵增控制在有限的范围内。
即席查询引擎架构从下到上可以分为:可插拔的异构存储层、屏蔽方言差异的数据查询层、承载表达能力的内存计算层。
图 4-1
4.1.1 异构存储层
异构存储方案是在引擎设计之初首先被确认的。因为 ADLM 数仓的数据规模和更新方式差距很大,既有日新增百 TB 的在线监控数据,也有频繁更新回溯的需求管理数据。此外,字节部分平台并没有直接提供数仓,而是以 OpenAPI 的方式提供数据输出的能力。
可插拔的异构存储设计,使数据选型得以在业务、数据、效率三者之间寻得平衡点。当前引擎支持 SQL 类、OpenAPI 类两大存储,未来还将支持 Graph、NoSQL 两大类。SQL 类因各数据库支持的 SQL 方言不同,进一步细分为 MySQL、ClickHouse、Slardar-Veno;OpenAPI 类支持 DataRocks、Tea、Metrics 等公司内部平台所开放的各类 OpenAPI。
4.1.2 数据查询层
在异构存储层之上,是数据查询层。查询层为上层的计算层屏蔽异构数据源之间的方言差异,并对计算层组装的查询语句进一步解析优化。查询层目前有 SQL-Manager、OpenAPI-Manager 两大模块,分别提供了各自的方言转译功能。方言转译功能在开发过程中完全遵循面向接口编程的原则。利用面向接口编程的特性,保证了业务间的解耦以及代码逻辑清晰、易维护。同时,该思想对测试更友好,也让引擎具备可插拔底层数据源的能力。
SQL-Manager 的设计目标,是希望通过利用自建查询层获得更丰富的数据表达能力、更良好的查询性能,以及更精细化的业务控制。SQL-Manager 的执行过程可分为串行的五个模块,分别是解析器 Parser、分析器 Analyzer、重建器 Rebuilder、优化器 Optimizer 和重写器 Rewriter。解析器通过 Antlr4 实现,将内存计算层的查询语言重新构建成 AST 抽象语法树。分析器将识别语法树中的业务逻辑,并完成相关处理。例如,DevMind 的 Cube 算子查询和 VirtualTable 改写就是在分析器中完成的。重建器用于标准 SQL 的构造及相关业务逻辑的拼接。优化器用于实现查询加速,最典型的场景是在多重查询场景中,实现算子下推。最后是重写器,负责将标准 SQL 转换为存储物理表 DB 对应的 SQL 方言。
图 4-2
4.1.3 内存计算层
内存计算层是数据可视化表达的核心,并为上层提供统一的查询语言。该层采用代数计算模型来突破 SQL 表达能力的边界,并以模块化的方式对外提供多种能力,其中在表达能力拓展上主要包括代数计算、Middleware 和 UDF 框架这三个部分。
代数计算:数据层返回的查询结果可以被视为一个标量或 N 阶向量的代数矩阵。当参与计算的矩阵各阶向量单位和量纲相同时,就能保证逻辑上的自洽。在内存计算层中,元指标/复合元指标均是纯粹的内存代数计算。其表达能力上限只受到当前模块支持的算术运算符和逻辑运算符的限制,因此理论上拥有近乎无限的表达能力。此外,纯内存代数计算非常适合为后续基于并行计算的计算加速。Middleware:查询层注定会承接大量的定制化需求,而定制化需求往往是一个系统“腐败”的诱因。因此,需要在计算层增加 Middleware 结构,分为 Pre、Sub 两个部分,分别用于处理 Request 和 Response。通过 Middleware 结构将定制化需求从系统的核心逻辑中释放出来,进而提升引擎整体扩展性和可维护性。其中一个典型的应用是各类枚举值的 Key 和 Value 转换,因为 KV 转换均在 Middleware 层执行,因此在数仓规范中可以明确枚举值,只需存储 Value,即可有效降低整体维护成本。UDF 框架:UDF 框架的设计目标是在内存计算层中提供一个统一的开发框架,令开发者可以通过低成本的框架实现各自的业务需求。希望在并不遥远的未来,DevMind 的即席查询引擎能够通过这种插件化的形式实现友好型架构的开发和设计,让集市而非大教堂赋予引擎更丰富的数据可视化分析表达能力。UDF 框架将整个处理过程分为七个步骤:参数校验、指标拆分、函数解析、并发查询、复合计算、结果合并、数据处理。在复合计算过程中,框架还为开发者提供了 Split、Aggr、PCT 等多种内建函数,以便提高开发效率。分析师用户在使用 UDF 函数时,使用方式与 MySQL 等 DB 的内置函数没有区别,上手成本仅限于函数入参和出参本身。当前,被 DevMind 广泛使用的函数有树形查询、数组型指标/维度、自然时间维度、工作量指标、人均型指标等。图 4-3
4.2 辅助分析算法
和 AIOps 不同,在数据分析场景中,辅助分析算法除了关注准确率和召回率,还需关注算法本身的可解释性和可验证性,从而降低理解成本,并提高结论的权威性。因此,在算法设计过程中,DevMind 将重新回归到经典的数理统计方法。
4.2.1 潜力分析
潜力分析是指在指标分析场景中,找出某个确定维度的维度项,改变它能对指标大盘产生最大的影响。
维度分项潜力值 = 维度分项贡献度 × 均值回归系数 × 稀疏维度处理
维度分项贡献度是指目标指标各维度的维度分项对大盘影响的贡献度。此处引入潜力分析的第一个假设:高占比项提升,使指定维度的各维度项变化同一固定比率,则高占比维度项对大盘的影响更大。均值回归系数源于潜力分析的第二个假设:均值回归,也就是假设维度分项数值都将围绕维度均值波动,波动趋近于均值的概率要高于背离均值的概率。同时,假设维度分项数值距离其均值越远,回归的概率越高。基于上述两点,算法设计了均值回归系数,且经过概率密度函数归一化处理。对于稀疏维度处理,举例说明,在分析 DAU 贡献度时,容易发现“性别”这一维度的维度分项贡献度会显著高于“城市”维度的维度分项贡献度。这是一个完全正确但是没有太多价值的发现,稀疏维度处理就是为了减少这类情况对分析结果的影响。4.2.2 比率型指标归因
在分析简单的数值型指标(如 DAU)时,常规的分析方式是首先进行维度分解,然后进行后续分析操作,例如贡献度计算、基尼系数计算。但是比率型指标具有不可加性的特点,不能直接进行维度分解。其不可加性的一个体现是除法对数值量纲的抵消,例如,大除大、小除小结果的量纲可能是相同的,而大除小、小除大的差异会被放大。此外,对于除法型复合指标还有另外一个问题——分子分母维度不对齐。
以每日客户端崩溃率 = 每日崩溃客户端数 ÷ 客户端DAU为例,每日崩溃客户端数包含特有维度崩溃类型,客户端DAU包含特有维度机型分档。
贡献度的核心假设是不同分项对大盘整体的影响不同。该假设与目标指标算子无关,因此对比率型指标进行合适的换算仍然适用于贡献度分析。为便于理解,此处引入代数向量空间的概念做进一步解释。
在向量空间中,大盘趋势是从 base 到 target 的向量baseAtargetA,同理可得维度项趋。
大盘趋势可由任意维度的维度项积分 得到,因此维度项贡献度之和为 1,baseAtargetA={j=1∫MbaseAijtargetAijdj…}i=1N。
根据贡献度的定义,当baseAtargetA和baseAijtargetAij的方向相反时,称维度项j为方向负贡献,当baseAijtargetAij和baseAtargetA的比值小于 1 时,称维度项j为大小负贡献。因此。只有在比值大于 1 时,维度项j可称为正贡献维度项。
为了与数值型指标贡献度相区分,定义除法型指标相关因子为贡献率。
相比于笛卡尔坐标系,贡献度更类似于极坐标系。当baseBtargetB和baseBijtargetBij的方向相反时,称维度项j为方向负贡献,当baseBijtargetBijbaseBtargetB⌢比值小于 1 时,称维度项j为角度负贡献。因此,只有在比值大于 1 时,维度项j可称为正贡献维度项。
贡献率的方向由维度项方向和大盘方向共同决定,当维度项方向与大盘方向不同时,则贡献率为负。公式如下:
CRij=△B△Bij=targetB÷baseBtargetBij÷baseBij×{−1,ifuij=u1,ifuij=u
4.3 房间里的灰犀牛——数据安全合规
在对研发效能领域进行数据分析时,不可避免地会涉及一些敏感数据。如果放任对敏感数据的滥用,那么极易造成潜在危机。就如同一个摆满瓷器的房间里,静静地站着一只灰犀牛,虽然此时此刻还没有一个瓷盘掉落在地,但整个房间被走动的灰犀牛摧毁是一个大概率会发生的事件。
如何在确保在数据使用绝对安全合规的前提下,提高数据分析的效率呢?
DevMind 对数据安全相关技术方案进行系统性集成,提出了基于 TEE 的在线隐私计算方案。方案分为事前、事中和事后三个阶段,分别对数据安全进行保护。
事前:通过非对称加密技术,使全过程数据始终在密文状态下存储、传输。事中:首先通过数据安全沙箱对运算的执行环境进行保护。然后基于联合查询方式,在保证数据存储安全的前提下提供近实时查询能力。最后对业务方查询语句进行动态解析,防范违规查询、差分攻击等有意或无意导致的数据泄露风险。事后:搭建字节内部联盟链,基于加密技术和区块链技术,将涉及高敏数据的操作全部上链,从而真正实现全部操作可监控、可追溯、可审计。图 4-4
第 5 章 数据模型层技术要点
5.1 数据分析类产品的误区与解决方案
5.1.1 专业性的僭妄
本小节的标题致敬了 1974 年哈耶克在诺贝尔经济学奖颁奖典礼上的演讲。哈耶克演讲的题目叫做《知识的僭妄》,其中“僭妄”意为“超越本分的妄为”。哈耶克演讲的主题是想表达市场是一种十分复杂的现象,永远不可能充分了解或计算一个过程产生的所有可能结果。因此需要对“貌似精确但很可能错误”的知识保持警惕。
无论是 Gartner 最近几年的趋势分析报告,还是作者对业界的持续观察。公民数据科学家,也就是没有严格数据统计背景的普通业务方,激发他们对业务数据分析的热情将带来极大的业务收益。但遗憾的是,当前业界数据分析类产品却普遍带有专业人士的“傲慢”,没有真正的用同理心去感受这些用户们的需求场景。
Gartner 于 2017 年提出“增强分析”的概念,并认为增强分析将颠覆现有的数据分析模式,成为数据和 BI 能力的第三大浪潮。根据 Gartner 的定义,增强分析是指面向广泛的业务用户、运营人员和公民数据科学家的下一代数据分析范式,通过机器学习实现数据准备、洞察发现、洞见分享等过程自动化。自增强分析的概念提出以来,Gartner 连续多年围绕这一主题发布多篇报告和讨论。
增强分析的核心思路是通过整合多种技术手段,提高数据分析过程的自动化程度和易用性,进而让更多业务相关方进入数据分析领域。但当深入到具体业务的实际工作场景中,会发现真正阻碍业务方自主进行数据分析的不是分析过程是否自动化和分析工具是否易用,而是可视化分析产品交互设计背后蕴藏的专业背景知识繁杂。例如,在使用大多数 BI 产品开始数据探索时,要首先选择并确定数据源,然后选择其附属的指标。这对专业数据分析师来说只是肌肉记忆,但普通用户会感到非常困惑:“我只想要一个指标结果,你却只能提供给我整个数据源”。再比如,BI 平台虽然有成熟的可视化交互方案,但普通用户仍然难以理解何谓“指标”“维度”“JOIN”等概念。
专业用户和普通用户对于数据分析类产品的需求南辕北辙,两者所对应的产品解决方案分别位于产品设计思路“光谱”的两端。专业用户需要的是绝对的表达能力自由度,并不介意产品上手时复杂的学习门槛。因此同样专业的数据分析类产品设计者能够理解用户所思所想,给出满意的产品方案设计,而这个方案的尽头会是 SQL、R 语言、MATLAB 等等。普通用户通常是一位带着问题的业务同学,希望凭借自己对业务的理解和一款简单上手的数据分析类产品,换来自己问题的答案。但是当前业界解决方案仍然有着较高的使用门槛。诸如简单的将“指标”、“维度”合成一栏,或是老罗的 TNT 这类 NLQ 方案,都不能真正的满足普通用户的需求。这些易用性的产品方案甚至还带有设计者专业性的傲慢。业务需求瞬息万变,不可能让有限的数据分析师来承载无限多复杂的业务分析需求。最终,所有人都会意识到需要激发每位业务相关方对数据分析的热情,挖掘其对业务理解的价值,创造一个真正的公民数据科学家的时代。
同时值得注意的是,数据分析所需复杂的数理统计知识并不能被精妙的交互设计消解。因此需要尽可能发挥 DevMind 覆盖从数据生产到数据消费的数据全生命周期优势,提供端到端的完整解决方案。解决方案有两个核心思路:一是将复杂业务场景和专业知识的理解成本尽量左移,为终端生产者屏蔽非必要的数据信息;二是将数据分析链路进行进一步的专业化分工,提升每个流程的生产效率。
5.1.2 专业化分工
关于上文提到的第一项解决方案“理解成本的左移”,将在后文“业务实践”部分中介绍。在此,首先讨论数据分析链路的“专业化分工”。
数据分析链路的专业化分工主要体现在由数仓工程师负责的数据生产端,以及由专业数据分析师主导的数据消费端。在 BI 等可视化分析过程中,通常由单一责任人完成数据探查、指标设计、可视化配置、报告呈现等工作。在 BI 平台中,这些工作的起点都是从选择数据源开始。这是一种典型的物质决定论,也就是首先明确底层的数据源,然后在此之上定义指标、维度等。以数据源为起点的交互,要求使用者是带着“问题”的,并且对数据源的数据结构有着清晰的认知。在第一步确定数据源后,在可视化分析过程中可以继续拆解后两步:第二步是完成最终物理执行的查询语句拼接,第三步是基于查询结果进行可视化组件适配。通常,这三个步骤在 BI 平台内部是杂糅在一起的,用户选择完数据源后去定义查询语言,如指标口径、特征维度,再适配可视化表达方式。这个过程中的难点在于第二步——查询语句的拼接,因为其要求执行者不仅理解数据源背后的数据模型,还要明晰当前要探查的业务对象。因此,即使是在 BI 平台的辅助下,通常也只能靠有限的数据分析师完成相关工作。
在中国,白内障手术只是一种非常普通的小手术。但是在印度,因为有经验的主治医师非常少,因此印度白内障手术费用远非底层穷人可以承受。印度亚拉文眼科医院学习麦当劳的模式,通过流水线分工的方式提高整个流程效率。在亚拉文眼科医院,主治医师只负责其中最关键的那一个步骤。一般的主治医生一年最多做 400 台手术,而亚拉文眼科医生一年能完成 2600 台手术。
DevMind 重新设计了数据可视化分析的交互方式,将传统的“以选择数据源为起点”改为“以指标为核心”。让富有经验的数据分析师和领域专家专注于核心指标及数据分析模型的设计。让更多的业务参与者围绕指标完成数据探查,或是进行衍生指标的再生产。
这一交互形式的改变不仅释放了领域专家的生产力,更重要的是,以指标为核心的交互大幅降低了非专业用户的理解和使用门槛。此外,生产形态的改变,令 DevMind 指标中台不再是旁路的元信息管理工具,而可以深度地参与到整个生产链路之中。因此,这一改变有效避免了在指标中台中初次沉淀的数据资产因脱离后续业务活动而逐渐腐败过期的问题。
5.2 元指标
基于上一节分析,最终设计以元指标作为 DevMind 平台产品在生产端和消费端用户交互路径的核心,同时,元指标也是整个 DevMind 底层数据模型中最重要的解决部分。
元指标的定义:元指标是指拥有完备业务含义且可独立执行的结构化表达式,其运行结果是带有物理含义的代数矩阵。其中,通过维度的数量来划分元指标的阶次。没有维度的元指标称为标量元指标 ,带有维度信息指标称为向量元指标 ,根据维度数量分为一阶向量、二阶向量等,如图 5-1 所示。
元指标的意义:与传统的 SQL 类 BI 平台生产侧交互逻辑围绕数据源展开所不同,在 DevMind 产品设计中,转为以元指标为核心的声明式表达。元指标的引入可以将整个数据分析过程从单一的数据分析师拆分为两个角色——负责指标生产的领域专家和负责数据分析的业务方。角色职责的内聚将有效提高数据分析全流程的效率。
在 DevMind 中正式使用元指标的概念时,遵循约定优于配置的原则,制定了两条基本约定。
约定一:元指标必须 带有时间范围修饰词,且位于特征第一位,且在后续注入维度的第一位。约定二:在配置元指标时需要指定度量对象修饰词,并由 DevMind 在消费场景中动态注入。图 5-1
(复合)元指标的定义:因为元指标的运行结果是带有物理含义的代数矩阵,因此从数学上理解,维度数相同且各阶维度单位及量纲相同的元指标可以进行代数运算。在业务上的约束则更为宽松,只要参与计算的两个元指标之一是另一方的全集,即可进行运算。因此,DevMind 将(复合)元指标定义为,由一个或若干个(复合)元指标经算术运算符或逻辑运算符组合而成的元指标。
(复合)元指标的意义:基于(复合)元指标,任何复杂的业务建模过程都可被视为纯粹的数学表达,因此其可以获得近乎无限的表达能力。
示例质效分=Sigmoid⎝⎜⎜⎜⎜⎛⎣⎢⎢⎢⎢⎡一月UGC需求数二月UGC需求数⋮十二月UGC需求数⋯⋯⋱⋯一月PGC需求数二月PGC需求数⋮十二月PGC需求数⎦⎥⎥⎥⎥⎤⎠⎟⎟⎟⎟⎞+arctan⎝⎜⎜⎜⎜⎛⎣⎢⎢⎢⎢⎡一月UGC事故数二月UGC事故数⋮十二月UGC事故数⋯⋯⋱⋯一月PGC事故数二月PGC事故数⋮十二月PGC事故数⎦⎥⎥⎥⎥⎤⎠⎟⎟⎟⎟⎞
公式 5-1
作为 DevMind 的业务目标,ADLM 包含四类度量对象,分别是业务线(树形)、产品(散点型)、部门/汇报线(树形)、个人(单点型)。因此,一个元指标在真正被用户消费时,至少要定义一个度量对象的维度字段。例如,用 AppID 维度作为产品度量字段,EmployeeID 维度作为个人度量字段。此外,要注意用于度量的维度会完全改变元指标的含义。例如,在对“事故总数”这一元指标进行业务线度量时,是采用事故责任业务线,还是事故受影响业务线,其含义有天壤之别。因此,在规范的元指标定义中,并不会存在以“事故总数”命名的元指标,只有“责任事故总数”和“受影响事故总数”。
5.3 数据仓库建设
数据是对业务的客观记录,数据模型是对业务模型的高层抽象,数据仓库分层则是对数据架构的解耦,从而保持清晰的数据结构。
数据仓库的分层方式有很多种,其中被广泛采用的方法是三层六类。三层六类中的“三层”是指操作数据层(ODS)、公共维度模型层(CDM)和应用数据层(ADS);“六类”是在三层基础上的进一步演化,操作数据层(ODS)独占一类,公共维度模型层(CDM)衍生为明细数据层(DWD)、汇总数据层(DWS)和数据维度层(DIM),应用数据层(ADS)衍生为数据集市层(DM)和应用数据层(ADS)。
在三层六类的架构划分中,DWD 层和 DWS 层用于清洗和提炼原始数据,并采用维度退化的方式将维度退化至事实表中,提高明细数据表的易用性。DM 层用于不同主题域间的数据链接,提高分析效率。ADS 层一般是结果类型数据,是对数据抽离分析程度最高的一层,通常作为前端应用直接读取的数据源。
三层六类的设计出发点就是解耦,让不同分层承载不同的业务目标,例如,ADS 层担负的是分析效率和查询速度的目标。但是过多的分层会有两个缺点,一个显性的缺点是数仓的分层需要数仓工程师维护,这是人力上的开销;另一个是隐性的缺点,过多的分层会导致数据团队和业务团队的矛盾。比如,如果数据团队强势,则按照规范分层取数降低业务迭代效率;如果业务团队强势,则业务直接下沉到 CDM 甚至 ODS 取数,使 ADS 层变得鸡肋,而指标口径也再次腐化。
DevMind 致力于构建 ADLM 全域数仓。该目标意味着数据首次提炼的成本和后续维护成本高昂。因此,DevMind 数仓建设思路围绕降低人力成本、提高可维护性展开。在具体的建设方式上,DevMind 以在线数仓为主,以离线数仓为辅。
离线数仓的建设都围绕着 DW 层,尤其是以 DWD 层为核心展开。离线层弱化了 DM 层作用,且几乎没有 ADS,同时维护公共 DIM 层串联全局链路。离线层的“弱 DM、轻 ADS”并不代表 DevMind 团队不认可三层六类的数仓分层设计,而是 DevMind 着重建设在线查询层,通过应用层物化视图、VirtualTable 等方式代偿了离线数仓 ADS、DM 层的能力。此外,为了加快查询速度,DevMind 实现了多种查询加速方式。
条件下推和剪枝:业务发展的趋势表达能力→导致查询复杂→耗时增长。为了打破这个链条,在 SQL-Manager 优化器部分开发了算子下推、谓词下推能力。在自卷积及 VirtualTable 场景中,将查询条件下推,以此减少查询整体的 Scan 数据量。Local/Short/Long Cache:缓存是查询引擎的必备组件之一。为了提高缓存命中率,根据下游数据源类型,为缓存时间设置了 Short/Long TTL 两种策略。对于写入/更新收敛在平台数仓内的数据,启用 Long TTL 策略。当底表数据发生更新时,调用缓存回调接口,更新任务的下游任务,对脏缓存进行清理。除基于 Redis 缓存外,DevMind 还提供了基于一个请求生命周期的 LocalCache,从而减少指标元信息在内业务信息的反复读取,并用于保存内存计算中间结果。应用层物化视图:大数据消费的一个主要难点在于,无限灵活的大数据消费场景和有限而昂贵的存储计算资源之间的矛盾。为此,在引擎中增加了应用层物化视图功能,由上层业务方显式开启,以此满足为特定目标数据的长期趋势构建图表进行可视化观察的需求。业务层预刷:针对诊断报告、管理驾驶舱等高价值、大计算量场景,需采用更精细化的预刷缓存策略,在性能上要求达到亚秒级响应、秒级呈现。除了减少数仓人力开销、降低维护成本等收益之外,以在线化为主的数仓建设方式的最大优点是,成功实现了将数据资产沉淀在指标上而非固化到数据集中。传统的将北极星指标固化到数据集中的做法实际是不够直观的,整个维护链路冗长,并且容易脱离关键生产链路。任何数据资产如果只是作为旁路记录,而不能真正参与到生产环境中,就注定会过期腐化。
图 5-2
第 6 章 应用层技术要点
6.1 全链路解决方案
现代工业化生产涉及流水线设计、供应链管理、进销存管理等多个方面。ERP 系统设计是为了帮助组织自动度量和管理核心业务流程,从而实现最优表现。ERP 系统通过协调公司中各个业务流程之间的数据流,提供单一事实源,并简化整个企业运营流程。
6.1.1 应用研发流程度量场景拆解
大型软件研发流程有其复杂性,但并没有显著的证据证明其复杂度远高于其他工业领域。为什么互联网行业并没有诞生类似于 ERP 系统的系统性应用研发流程度量解决方案呢?一个可能的原因在于工业界的流程和环节的建设周期相对较长,针对一条产品线定制化建设一套 ERP 系统的边际成本较低。同时,通过一些 Low-Code 解决方案能够进一步降低构建成本。但是对于互联网应用软件研发流程来说,其软件开发的本质是人与人之间的协作,而非流水线节点之间的桥接。因此,应用软件研发生命周期各个环节所构建的 DAG 图,不可避免地具有易变性和多样性。
以抖音某子方向百人研发业务团队为例,该团队同时使用的需求流程模板达到 5 个,一年中使用过但后续废弃的需求流程模板超过 20 个。流程模板在不断兴起泯灭,但是在流程模板之上创建的需求实例真实地固化了一个需求的元信息。DevMind 需要做的就是,从这片混沌的原始数据中提炼出有价值的信息。所以,对于应用软件研发效能的度量,其难度在于对易变流程的统一抽象。
图 6-1
在字节跳动的实践中,DevMind 将应用软件研发流程的业务模型抽象到三个层次——需求、技术栈、成员。例如,一个“评论支持点赞功能”的需求涉及 iOS、Android 两个技术栈,其中,iOS 由张三、李四两位同学负责,Android 是由小 A、小 B、小 C 三位同学负责。上述是 DevMind 在流程度量模型中的最简表达形式,其中的里程碑节点、阶段耗时、工作量都可以在三个层次中分别表达。
在确定业务模型后的数据模型设计中,DevMind 围绕笛卡尔模型展开。笛卡尔积又称直积,笛卡尔积的结果是所有参与计算维度的维度项的全排列。正因为笛卡尔积基于维度项全展开的特性,在行式存储数据库中,笛卡尔积模型能够有效地表达 DAG 图等复杂的业务模型。笛卡尔积模型通常在“数据建模 101”课程中会被提到,但在实际工业生产中却鲜有使用。无论是范式建模还是维度建模,都不推荐使用笛卡尔积模型。这是因为,在各建模理论中都强调单表实体项的唯一性,比如通过雪花模型实现层级展开的效果。除了理论上的约束,在实际使用时,笛卡尔积模型表要求查询者对表结构非常了解,且有较高的数据分析技巧。
图 6-2
6.1.2 DAG 场景全链路解决方案
DevMind 设计的目标是转动研发效能提升的飞轮,因此不仅要解决问题,还要降低解决方案的使用成本,直到普通生产者用户可以承受为止。针对 DAG 场景,DevMind 提出了一种超越范式的系统性解决方案。这个方案也很好地体现了 DevMind 通过对数据生产分析全链路的掌控,降低终端用户使用成本的实践思路。
必须承认的是,业务自身的复杂性无法通过业务模型、数据模型的精妙设计完全消解。复杂的业务势必会带来复杂的业务模型、数据模型。对于下游数据分析者来说,这种数据模型有着极高的理解门槛。无论是指标设计阶段用户,还是后续业务分析阶段用户,都会受其困扰。虽然 DevMind 无法完全消解业务的复杂性,但是可以将理解成本尽可能左移,从而降低数据分析链路右端的终端用户理解成本。DAG 场景的数据分析链路分为三个阶段:数据模型设计阶段、业务指标配置阶段、业务数据分析和消费阶段。
数据模型设计阶段:数据模型设计者应对业务模型有深刻 的理解。因为设计者将负责进行业务模型的抽象,并将其转换为基于笛卡尔积展开的数据模型。在数据模型设计中要注意两点。第一点是确定笛卡尔积层次的深度和维度顺序,例如,研发流程度量分为需求、技术栈、成员三层。第二点是为存在分支的模型明确分支策略,例如,事故度量中的第一层是事故,第二层是独立并行的两个维度——事故方和改进计划。业务指标配置阶段:指标定义者需要对笛卡尔积数据模型层次有基本 的理解。定义者要在配置中遵循相应的分层规范,无法逆层级定义指标。同时,为了防止指标定义者对数据模型理解存在偏差,导致配置错误。在指标配置阶段,即席查询引擎不但能提供事后的配置规范检查,还能在事中进行配置层级维度推荐。业务数据分析和消费阶段:自助探索分析的普通用户无需感知底层数据模型的差异,在使用上与普通数据模型几乎完全一致。这是因为在查询时,DevMind 的即席查询引擎会对查询语句指标和维度所属层级进行推测,随后隐式地完成笛卡尔积上卷。图 6-3
6.1.3 基于 Cube 模型全链路解决方案
除此之外,DevMind 在多个业务场景中运用了基于数据生产分析全链路掌控力,将整体成本左移的方案。例如,DevMind 基于 Cube 模型的超大规模数据在线分析方案。
对于 ADLM 全域的研发效能度量,其中 App 的流畅度、稳定性、成功率、崩溃率、耗电量等线上指标都是考核的重点。为了构建准确度量 App 品质的北极星指标,字节客户端团队设计了用户“可感知评分”。为了做到客观全面,该评分包含众多端监控指标。这会造成链路长、数据量大、公式复杂等技术挑战。 其中,仅 CN 机房每日客户端上报的原始数据就达 100TB 以上。
图 6-4
在数仓建设中,将 Cube 定义为在事实表和维度表之上,将目标分析维度的各个可枚举维度成员的组合,其聚合结果集即为多维数据模型 DataCube。Cube 模型以预聚合的方式有效节省存储空间,因 Scan 数据量的减少,查询侧也得到有效加速。因此,在 ADLM 全域度量中,对于超大数据量在线的情况,非常适宜引入 Cube 模型。
DevMind 同样将全过程分为三步:模型设计、指标定义、数据消费。在模型设计过程中,设计者确定 Cube 粒度,以及涉及的维度组合。在数据生产过程中,完成所指定的 Cube 粒度聚合和剪枝优化;在指标定义时,定义者仅会基于已有的组合进行指标和维度设计;在数据消费阶段,用户自助分析时由即席查询引擎自动注入 CuboID,从而使用户(消费者)无须感知底层模型的复杂性。
图 6-5
6.2 树形结构查询
在研发效能分析场景中,度量对象通常是树形结构,例如部门、组织架构、汇报关系等。同时,因为公司组织架构调整、人员离职/入职均会对树形结构产生影响,因此如果仅选择一个时间切面进行分析,无法反映真实情况。因此,算法不仅要考虑树形结构中父子节点的空间 关系,还需兼顾父子节点的时效 关系。
6.2.1 场景案例
为便于理解,此处举一个情景化的例子。某研发团队负责人小 A 通知下属业务线负责人小 B 和小 C,需要统计这两个团队 7 月的 BUG 数。小 B 团队的小 D 在 7 月引入 5 个 BUG。小 B 团队的小 E 在 7 月共引入 4 个 BUG,其中 1 个 BUG 是在 7 月 1 日引入的,另外 3 个是 7 月 8 日转岗到小 C 团队后引入的。小 C 团队的小 F 和小 G 在 7 月分别引入 4 个 BUG。因此在 7 月,小 B 团队共引入 6 个 BUG,而小 C 团队引入 11 个 BUG。
图 6-6
在研发效能度量过程时,切忌基于数值型指标直接进行比较。在上述例子里,小 C 团队的 BUG 数看上去要远多于小 B 团队,但如果将度量视角从“团队 BUG 数”切换到“团队人均 BUG 数”,就会得出另一个结论。虽然软件研发是强创造性的脑力劳动,但基于均值回归假设,DevMind 假定百人以上的研发团队的产出与人数是强相关的。基于上述假设,DevMind 将团队成员理论可到岗天数之和作为团队人均类指标的分母。在例子中,7 月 1 日至 7 月 7 日共 5 个工作日,7 月 8 日至 7 月 31 日共 15 个工作日。因此,小 E 在小 B 团队是 0.25 人力,而在小 C 团队是 0.75 人力。最终,小 C 团队的人均 BUG 数是 4,而小 B 团队的人均 BUG 数则达到 4.8。
图 6-7
此外,因为树形结构广泛存在于研发效能度量场景中,算法还需对业务指标具备普适性和可泛化的能力。无论是数值型指标事故数、比率型指标需求吞吐率,还是更复杂的质量分,其指标的度量对象都是树形结构对象。
树形结构的业务对象很适合采用 MongoDB 等文档型数据库来表达,但是当树形结构作为统计分析对象时,文档数据库的表达能力就远远不够了。最终,DevMind 选用 MySQL、ClickHouse 两个 OLTP、OLAP 的典型代表作为分析对象的存储引擎。
6.2.2 三级优化方案
树形结构查询最大的挑战在于,随着节点规模的不断增大,查询 SQL 也会不断地成倍膨胀。SQL 膨胀不仅会使人员信息增多,导致查询性能验证劣化,SQL 长度过长还会导致 DB 的 AST 语法树构建出现难以处理的问题。面对性能和语法树长度的问题,DevMind 采用三级优化方案。
内存计算辅助查询:在内存中对查询结果进行剪枝,排除不符合条件的数据;在内存中对剪枝后的结果进行指标计算。但是,该优化方案收益会随着节点规模的扩大而收窄。因为详细数据查询涉及大量回表 SCAN 操作,同时在 ClickHouse 场景中,对大量明细数据查询场景支持力度有限。构建索引列:在树形结构查询中,无论是部门树还是汇报线树,都比查询指标的业务数据体量小得多。因此,直接在内存中维护这两棵树。同时,树的叶子节点和父节点的比例在 10:1 以上,当查询层级被圈定为非叶子节点时,查询规模则可从万级节点降为千级节点。因为一个人员在确定的时间点,其所属部门和汇报关系都是确定的,所以可通过直接构建非叶子节点为索引列的方式来加速查询。但是,该方案会引入索引列构建和维护成本。查询请求拆分:分为基于时间的纵向拆分和基于树形结构的横向拆分。基于时间的拆分,当查询数据的时间范围超过一定规模的时候,将数据按照查询的时间进行切分,比如,在查询一年数据时,将其拆分为 12 个月粒度数据;基于树形结构的拆分,将查询范围下放到下一级节点,比如,在查询 N 级的节点时,可以将其拆解成 N-1 级节点的多个查询。在进行树形结构拆分时发现同一层级的节点规模差异巨大,如果直接进行拆分会导致并发请求量过大,所以此处引入限定容量 01 背包算法,动态生成查询合并方案,尽可能保证各子查询规模近似相等。第 7 章 未来展望
技术侧迭代发展规划首先不能脱离业务需求场景,没有业务落地场景和业务收益支撑的技术方案,只能是闭门造车、无法落地。其次,DevMind 实质上是一种通用的数字化解决方案,其底层的数据中台模式和 ABI 产品内核为后续技术演进提供了良好的基础。面对上层的 ADLM 研发效能度量业务域和下层的数据中台模式,DevMind 提出了“工型战略”。工型战略意为:在上横业务场景和下横架构底座之间,发展业务亟需的各类垂直技术能力,在多个不同领域的单点分别突破,取得收益后再持续演进,最后在演进过程中实现技术点的交叉,从而连点成面。
在可预见的未来,DevMind 技术驱动业务增长三驾马车分别是:即席查询引擎、数据科学、图分析。
图 7-1
7.1 即席查询引擎
为了转动研发效能提升的飞轮,在运营策略上,DevMind 通过降低分析型用户的使用门槛,进而引爆消费者市场。即席查询引擎是整个数据可视化分析中最核心的一环。DevMind 后续的迭代也将围绕提升数据表达能力展开。
当前的即席查询引擎架构可以简单表述为:查询层提取各 SQL 类存储引擎数据,再到计算层的内存代数计算。计算层的代数运算是纯粹的数学表达,其表达能力可满足后续较长时间的业务需求。而查询层受限于 SQL 语言本身有限的表达能力,以及 SQL 类存储引擎架构特有的局限性。
对于 QL 类存储引擎来说,无论是行式存储的 B+树索引回源 TablePage,还是列式存储的 LDM 树搭配垂直分表、全列索引,探究其设计的核心目标都是摆脱来自机械硬盘的物理桎梏,也就是尽可能提高扫描(Scan)效率。但是面对复杂场景,比如研发效能场景树形对象,传统的 SQL 类存储引擎就显得力有不逮。
计算机存储介质从 HDD 的磁盘发展到 SSD 的 NAND Flash,再到 AEP 的 SCM。在存储介质演进的技术浪潮之下,我们完全可以预见下一代在线分析型数据库的发展,大概率会出现一款存算分离的键值型存储系统。DevMind 即席查询引擎的计算层是天然的内存运算,异构存储层是支持低成本的映入键值存储系统,查询层支持对复杂对象的过滤查询。DevMind 查询层需要的是在复杂过滤条件下的键值查询。其实现思路有两种:第一种是自研 DSL 语言,完成从计算层到底层的键值存储,如 RocksDB 数据查询;第二种是使用类似 Calcite 等中间件作为胶水层,继续沿用 SQL 语言。这样可以充分发挥内存型数据库的性能优势,同时减少对系统的侵入。
除了 DSL+KVDB 的方案,图数据库也是可以重点探索的方向。例如,面向需求项目管理的小型 DAG 图场景,或整个研发效能链路的大型 DAG 场景。图数据库天然地能将 DAG 模板用点边表达,但是实际分析场景最多是求取从聚簇到聚簇之间的聚合结果。当前使用 Gremlin 在线图数据库并不能很好地表达包括Group By在内的聚合语义。因此在技术选型时,可能会选用 Flink Gelly 等更灵活的语义方案。
7.2 数据科学
即席查询引擎的优化是为了更好地回答 What 的问题,关于 Why、How、What if 等更深层次的问题则依赖 DevMind 在数据科学方面工作。
Why——诊断型分析,是指对现状(如异常波动)进行诊断定位,直接给出具体原因。数据分析是指通过使用数据来发现问题和趋势,并将原始数据转化为可行的见解。数据分析可以帮助团队更清晰、深入地了解业务,并为决策提供数据支撑。用于业务决策的数据分析算法主要是基于经典数理统计的白盒算法,而非基于拟合的黑盒算法。这是因为,在诊断型分析算法设计过程中,除了关注准确率和召回率,还要更多关注算法本身的可解释性。DevMind 当前适配了多种时间序列指标的归因分析,如比率型、复合运算型。后续,DevMind 将开展指标关联分析、复杂数据模型自动分析,以及对几类经典数据挖掘算法的产品化建设。How——建议型分析,是指在理解客观现状、定位问题原因之后给出解决方案。就像乘客购买高铁票通常不是为了欣赏沿途的风景,而是想尽快到达目的地。用户打开 DevMind 的根本目的是寻求问题的解决方案,而不是体验良好的产品交互设计。字节跳动内部有着众多的产品线、业务线,当研发效能提升飞轮转动之后,将在 DevMind 中沉淀大量的业务成功实践案例。这些实践案例及专家分析同样对其他业务团队有着很重要的参考意义。DevMind 将把沉淀在平台内的宝贵知识收集起来,构建一套专家系统。专家系统分为两大部分——知识库和推理机。知识库主要负责 DevMind 已有专家分析、实践案例的收集和模块化表达,既包括物质及概念等实体的知识,也包括专家特有的经验法则和判断力。推理机用于根据算法或决策策略,完成与知识库内各项专门知识的推论,依据用户的问题来推得正确答案。What if——推演型分析,是指基于历史数据和算法,假定引入某个主动型变化后,分析对业务可能产生的定量影响。研发效能场景不同于 toC 的 App 拥有巨大的流量池,可以进行 A/B 实验。一项重大的技术重构对后续研发效率是否有提升、提升多少,这些问题无法通过将研发团队分成两组、进行对照比较来解决。另一个典型的场景是,大业务线研发负责人想要调整某中台部门对业务线的支持力度,需要分析哪些业务线会受益、哪些会受损。上述都是无法使用 A/B 实验的推演型分析场景。在推演型分析中,比较典型的例子是 2021 年诺贝尔经济学奖获得者安格里斯特和因本斯在因果推断方面的工作。在这些需求场景之上,再加上小样本量的限制,又会将问题的复杂度提升到新的台阶。It's a road less traveled, but I still move forward。7.3 图分析
整个应用软件研发生命周期可以视为一种 DAG 图,其天然适配图数据库的点边模型。基于图数据库的图分析,既适用于对单个需求全流程的微观分析,也可应用于整个业务线状态流转桑基图等宏观分析。此外,再结合数据挖掘算法,图分析可以在共性问题洞察方面为研发效能度量的分析过程带来显著变化。
后记
本文虽着眼于字节跳动研发效能平台 DevMind 的技术发展历程,但希望借此能为广大读者朋友提供一份普适的数字化解决方案。
通过高内聚低耦合的设计来应对无限多变的业务场景,是度量团队立项之初的野望。也正因为此,我们选择了数据中台模式实现数据资产沉淀,数据生产消费全链路解决方案消解复杂业务分析场景。我们也因此洞见了降低普通用户数据分析门槛后所带来的巨大业务价值。此外,DevMind 其 SaaS 化的产品架构设计,也为其提供了第二增长曲线的可能。This would be another story。
作者: 姜磊,字节跳动效能度量平台(DevMind)技术负责人
英语晨读Day 14:辞职,努力
友课MBA加油站:第734期英语晨读
今日词汇: 辞职,努力
▪ resign;retire;depart;apart
▪ participate;vain;futile
▪strive;contrive;endeavor
1.resign [rɪ’zaɪn]
v. 辞职;辞去(职务);放弃
He resigned from the company in order to take a more challenging job.
他从这家公司辞职以便能够从事更有挑战性的工作。
2. retire [rɪ’taɪə]
v. 退休;退职;退役
Since retiring from the company, she has done voluntary work for a charity.
从公司退休后,她就一直在为一家慈善机构做志愿工作。
3. depart [dɪ'pɑːt]
vi. 离开,出发,去世
Trains for Cambridge depart every half hour from platform 9.
开往剑桥的火车每半小时一趟,从9站台出发。
departure n. 离开,出发
4. apart [ə'pɑːt]
adv. 分开;相隔,远离
She tore the chicken apart and began to eat.
她把鸡撕成块,然后开始吃。
apart from 除… 之外
Apart from a few words, I do not know any French at all.
除了很少的几句外,我对法语一无所知。
5. participate [pɑː'tɪsɪpeɪt]
v. 参与,分享
participant n. 参与者
She never participates in any of our discussions, does she?
她从来不参加任何讨论,对吗?
6. vain [veɪn]
adj. 徒劳的,自负的
our efforts are not in vain.
我们的努力没有白费。
vanity n. 虚荣
7. futile ['fjuːtaɪl]
adj. 无用的,徒劳的
It was futile to continue the negotiations.
继续谈判下去也是枉然。
8. strive [straɪv]
vi. (尤指长期地和不畏艰难地)努力,奋斗,抗争
We need to strive to narrow the gap between the rich and the poor.
我们要努力缩小贫富差距。
9. contrive [kən’traɪv]
v. 设计,发明;设法做到;谋划
Somehow she contrived to get tickets for the concert.
她不知用什么方法弄到了音乐会的票。
10. endeavor [ɪn’devə]
v. /n. 努力,尽力
I endeavored to explain the legal consequences of his action.
我努力解释他的行为会带来的法律后果。
In spite of our best endeavors, it has been impossible to contact her.
尽管我们尽了最大的努力,但还是联系不到她。
//点击“阅读原文” 可查看往期全部晨读汇总
拓展阅读
讲义词汇:Day10丨Day11丨Day12丨Day13
考研高频词:day1丨day2丨day3丨day4
相关问答
KNC的平板电脑如何进入recovery方式-ZOL问答KNC进入Recovery模式首先,请关闭你的KNC,这个真心好办,按然后选择关机(PowerOff)等候你的手机关机,然后等上十几二十秒,这样确保设备的确已关机现在同时按住...
KNC平板电脑没法开机-ZOL问答adbshell"echo-nboot-recovery|busyboxddof=/dev/block/nandfcount=1conv=sync;sync;rebo...
dev c+使用教程?您好,DevC++是一个集成开发环境(IDE),用于C和C++编程。以下是一个简单的DevC++使用教程:1.下载和安装:访问DevC++官方网站(https://so...
电脑用着突然蓝屏,重启出现Reboot and Se|ect ProPer Boot dev ?操作系统异常,个人建议:首先通过F8进入安全模式。如果你不了解是哪方面问题,可以下个腾讯电脑管家。该软件体检功能应该是可以修复系统异常。同时,该软件还...
GOV"是什么意思?意思是政府部门。双语例句:1、ButourcorrespondentsaystherealreasonGovSchwarzeneggerwantsthechangeism...
刚装了一台电脑,装好后显示器显示:REBOOT AND SELECT PROPER?这是开机找不到可以启动设备的提示。rebootandselectproperbootdeviceorinsertbootmediainselectedbootdevicea...
...thescopeandfunctionalitiesprovidedbyaparticulardev_作业帮[回答]详细的商业规格也称为:要求的规格说明阶段协助管理一声也发不出的选择,一种数量的业务系统的玩法,每提供的范围和功能描述被某个特定的开发和实现方...
该如何学习python?python前景怎么样?1、选择Python版本并且安装开发环境。对于Python工程师来说,Python的版本则是你们的工作环境。所以在学习之前一定要考虑选择一个合适自己的版本,Python3对零基...
一个app退出时连带把另一个app也退出了 - OSCHINA - 中文开...android新手一个,请大家指点下,该从哪里入手解这个bug呢?是不是放送广播了...Solon有企业版2.8万起步价(多一个,开发票+企业专属群)。不需要靠文档收钱的...
国际上知名的DevOps相关大会有哪些?DevOpsDayDevOps国际峰会Cloud&DevOpsWorldDevOpsEnterpriseSummit(DOES)O’ReillyVelocityCon...