Orca:大数据模块化查询优化器体系架构

大数据 数据分析
优秀的和平庸的优化器之间的差异一直是众所周知的[。但是,这些系统必须处理的数据量越来越大,从而加剧了优化错误,并比以往任何时候都需要强调查询优化的重要性。

大数据系统的流行引起了人们对查询优化的新兴趣,因为新型的数据管理系统在前所未有的可扩展性,可用性和处理能力方面取得了突破,通过SQL或类似SQL的接口,可以轻松访问数百TB甚至PB的大型数据集进行分析。优秀的和平庸的优化器之间的差异一直是众所周知的[。但是,这些系统必须处理的数据量越来越大,从而加剧了优化错误,并比以往任何时候都需要强调查询优化的重要性。

[[385646]]

好的查询优化器在架构设计上需要考虑以下的几个方面:

  • 模块化。使用元数据和系统描述的高度可扩展的抽象,不再局限于特定的主机系统,如传统的优化器。相反,可以通过数据支持的插件将其快速移植到其他数据管理系统。
  • 可扩展性。通过将查询及其优化的所有元素表示为具有同等地位的一等公民,避免多阶段优化的陷阱,在此阶段,某些优化将在事后进行处理。众所周知,多阶段优化器难以扩展,因为新的优化或查询构造通常与先前设置的阶段边界不匹配。
  • 支持多核架构。 系统需要部署一个高效的多核感知调度程序,该调度程序可在多个内核之间分配各个细粒度的优化子任务,以加快优化过程。
  • 可验证性。有确定的特殊规定内置机制级别来保证正确性和性能。除了改善工程实践之外,这些工具还使人们能够高度自信地进行快速开发,并缩短了新功能和错误修复的周转时间。
  • 性能。 查询的性能是我们最希望等到的结果

一、MPP架构

Orca是Pivatol开发的大数据模块化查询优化器,被用于Greemplum和Hawq之中。他就是按照上面的要求来设计的。

上图是Greemplum的体系架构,通过在多个服务器或主机之间分配负载以创建单个数据库阵列来处理大量数据的存储和处理,所有这些数据库共同工作以呈现单个数据库入口。 主节点是GPDB的入口点,客户端可以在此连接并提交SQL语句。 主节点与其他数据库实例(称为段)协同工作,以处理数据处理和存储。 当查询提交给主节点时,将对其进行优化并将其分解为较小的查询,这些较小的查询将被分配给各部分,以共同协作以交付最终结果。 互连使用标准的千兆以太网交换结构,负责各段之间的进程间通信的联网层。

在查询执行期间,可以通过多种方式将数据分布到段,包括散列分布,其中元组根据某种哈希函数分布到段,复制分布,其中表的完整副本存储在每个段和单例分布,其中整个分布式表从多个段收集到单个主机(通常是主节点)。

二、SQL on Hadoop 架构

在Hadoop上处理分析查询正变得越来越流行。最初,查询表示为MapReduce工作和Hadoop的吸引力在于其可扩展性和容错能力。编码,手动优化和维护MapReduce中的复杂查询非常困难,因此像类似SQL的声明性语言是在Hadoop之上开发的。 HiveQL查询被编译到MapReduce作业中,并由Hadoop执行。 HiveQL加快了复杂查询的编码速度,但同时也很明显地表明,Hadoop生态系统,因为已编译的MapReduce作业显示了较差的性能。

Pivotal通过引入HAWQ 来应对挑战,它是一种在HDFS之上的大规模并行SQL兼容引擎。 HAWQ以Orca为核心来设计有效的查询计划,从而最大程度地降低访问Hadoop集群中数据的成本。 HAWQ的体系结构将创新的基于成本的最先进的优化器与Hadoop的可伸缩性和容错能力相结合,以实现PB级数据的交互式处理。

最近,包括Cloudera的Impala和Facebook的Presto在内的许多其他努力引入了新的优化器,以在Hadoop上应用SQL处理。当前,这些工作仅支持SQL标准功能的一部分,并且其优化仅限于基于规则的。相比之下,HAWQ具有完全符合标准的SQL接口和基于成本的优化器,这两者都是Hadoop查询引擎中前所未有的功能。

三、Orca的架构

Orca是Pivotal研发的数据管理产品(包括GPDB和HAWQ)的新查询优化器。 Orca是基于Cascades优化框架的现代的自上而下的查询优化器。 尽管许多Cascades优化器与主机系统紧密耦合,但是Orca的独特功能是它能够作为独立的优化器在数据库系统之外运行。 此功能对于使用一个优化程序支持具有不同计算架构(例如MPP和Hadoop)的产品至关重要。

1. DXL

将优化器与数据库系统分离,需要建立一种通信机制来处理查询。 Orca包括一个用于在优化器和数据库系统之间交换信息的框架,称为数据交换语言(DXL)。 该框架使用基于XML的语言对通信所需的信息进行编码,例如输入查询,输出计划和元数据。 覆盖在DXL上的是一种简单的通信协议,用于发送初始查询结构并检索优化的计划。 DXL的主要优点是将Orca包装为独立产品。

Orca的输入是DXL查询,Orca的输出是DXL计划。在优化期间,可以查询数据库系统以获取元数据(例如,表定义)。 Orca通过允许数据库系统注册元数据提供程序(MD Provider)来抽象化元数据访问详细信息,该数据提供程序负责将元数据序列化为DXL,然后再发送给Orca。还可以从包含以DXL格式序列化的元数据对象的常规文件中使用元数据。

数据库系统需要包括使用/发送DXL格式数据的转换器。 Query2DXL转换器将查询分析树转换为DXL查询,而DXL2Plan转换器将DXL计划转换为可执行计划。此类转换器的实现完全在Orca之外完成,这允许多个系统通过提供适当的转换器来使用Orca。 Orca的体系结构具有高度的可扩展性。所有组件均可单独更换并单独配置。

2. Orca的不同组件包

(1) Memo

由优化器生成的计划替代方案的空间被编码在称为Memo 的紧凑型内存数据结构中。 Memo结构由一组称为组的容器组成,其中每个组包含逻辑上等效的表达式。 Memo组捕获查询的不同子目标(例如,表上的过滤器或两个表的联接)。 称为组表达式的组成员以不同的逻辑方式(例如,不同的连接顺序)实现组目标。 每个组表达式都是一个运算符,具有其他组作为其子级。 Memo的这种递归结构允许对可能的计划的巨大空间进行紧凑的编码。

(2) 搜索和作业计划程序

Orca使用搜索机制在可能的计划替代方案的空间中导航,并以最低的估计成本确定计划。 搜索机制由专门的Job Scheduler启用,该Job Scheduler通过以下三个主要步骤来创建依赖或并行工作单元以执行查询优化:探索(在其中生成等效的逻辑表达式),实现(在其中生成物理计划)和优化(在所需的物理属性中,例如,排序顺序),并计划替代方案的成本。

(3) 变形

通过应用可以产生等效逻辑表达式(例如,InnerJoin(A,B)→InnerJoin(B,A))或现有表达式的物理实现(例如,Join(A,B)的转换规则,可以生成计划替代方案 )→HashJoin(A,B))。 应用转换规则的结果将复制到Memo中,这可能会导致创建新组和/或向现有组添加新的组表达式。 每个转换规则都是一个独立的组件,可以在Orca配置中显式激活/禁用它。

(4) 属性强制

Orca包括一个可扩展的框架,用于根据正式的属性规范描述查询要求和计划特征。 属性具有不同的类型,包括逻辑属性(例如,输出列),物理属性(例如,排序顺序和数据分布)和标量属性(例如,连接条件中使用的列)。 在查询优化期间,每个运算符都可以从其子级请求特定的属性。 优化的子计划可能自己满足所需的属性(例如,IndexScan计划提供排序的数据),或者需要在计划中插入强制程序(例如,Sort运算符)以交付所需的属性。 该框架允许每个操作符根据子计划的属性和操作符的本地行为来控制执行的位置。

(5) 元数据缓存

由于元数据(例如,表定义)很少更改,因此在每次查询时都将其传送会产生开销。 Orca在优化器端缓存元数据,并且仅当缓存中不可用或自上次将其加载到缓存以来发生更改时才从目录中检索元数据。 元数据缓存还从优化器中提取数据库系统详细信息,这在测试和调试期间特别有用。

(6) GPOS

为了与可能具有不同API的操作系统进行交互,Orca使用了称为GPOS的OS抽象层。 GPOS层为Orca提供了广泛的基础架构,包括内存管理器,用于并发控制,异常处理,文件I / O和同步数据结构的原语。

四、查询优化

1. 工作流

我们使用下面的查询来解释查询优化的流程:

  1. SELECT T1.a FROM T1, T2 
  2. WHERE T1.a = T2.b 
  3. ORDER BY T1.a; 

其中T1的分布为Hashed(T1.a),而T2的分布为Hashed(T2.a)。

下面的XML显示了DXL中上一个查询的表示形式,其中我们提供了所需的输出列,排序列,数据分布和逻辑查询。元数据(例如表格和运算符定义)都装饰有元数据ID(Mdid),以允许在优化过程中请求更多信息。 Mdid是由数据库系统标识符,对象标识符和版本号组成的唯一标识符。例如,“ 0.96.1.0”是指GPDB版本为“ 1.0”的整数相等运算符。元数据版本用于使已跨查询进行修改的缓存元数据对象无效。

  1. <? xml version =" 1.0 " encoding =" UTF -8 "> 
  2. < dxl:DXLMessage xmlns:dxl =" http: // greenplum . com / dxl / v1 " > 
  3.   < dxl:Query > 
  4.     < dxl:OutputColumns > 
  5.         < dxl:Ident ColId ="0" Name ="a" Mdid =" 0.23.1.0 "> 
  6.     </ dxl:OutputColumns > 
  7.     < dxl:SortingColumnList > 
  8.         < dxl:SortingColumn ColId ="0" OpMdid =" 0.97.1.0 " > 
  9.     </ dxl:SortingColumnList > 
  10.     < dxl:Distribution Type =" Singleton " / > 
  11.     < dxl:LogicalJoin JoinType =" Inner " > 
  12.       < dxl:LogicalGet > 
  13.         < dxl:TableDescriptor Mdid =" 0.1639448.1.1 " Name =" T1 " > 
  14.           < dxl:Columns > 
  15.             < dxl:Ident ColId ="0" Name ="a" Mdid =" 0.23.1.0 "> 
  16.             < dxl:Ident ColId ="1" Name ="b" Mdid =" 0.23.1.0 "> 
  17.           </ dxl:Columns > 
  18.         </ dxl:TableDescriptor > 
  19.       </ dxl:LogicalGet > 
  20.     < dxl:LogicalGet > 
  21.       < dxl:TableDescriptor Mdid =" 0.2868145.1.1 " Name =" T2 " > 
  22.         < dxl:Columns > 
  23.             < dxl:Ident ColId ="2" Name ="a" Mdid =" 0.23.1.0 "> 
  24.             < dxl:Ident ColId ="3" Name ="b" Mdid =" 0.23.1.0 "> 
  25.         </ dxl:Columns > 
  26.       </ dxl:TableDescriptor > 
  27.     </ dxl:LogicalGet > 
  28.       < dxl:Comparison Operator ="=" Mdid =" 0.96.1.0 " > 
  29.         < dxl:Ident ColId ="0" Name ="a" Mdid =" 0.23.1.0 "> 
  30.         < dxl:Ident ColId ="3" Name ="b" Mdid =" 0.23.1.0 "> 
  31.       </ dxl:Comparison > 
  32.     </ dxl:LogicalJoin > 
  33.   </ dxl:Query > 
  34. </ dxl:DXLMessage > 

DXL查询消息被传送到Orca,在该消息中被解析并转换为内存中逻辑表达式树,该树被复制到备忘录中。下图显示了备忘录的初始内容。逻辑表达式为两个表和InnerJoin操作创建三个组。为了简洁起见,我们省略了加入条件。组0称为根组,因为它对应于逻辑组的根表达。逻辑表达式中运算符之间的依赖关系被捕获为组之间的引用。例如,InnerJoin [1,2]将组1和组2称为子级。如以下步骤所述进行优化。

(1)探索。 触发生成逻辑等效表达式的转换规则。 例如,将触发一个Join Commutativity规则,以从InnerJoin [1,2]中生成InnerJoin [2,1]。 探索的结果是将新的组表达式添加到现有组中,并可能创建新的组。 Memo结构具有基于表达式拓扑的内置重复检测机制,以检测和消除由不同转换创建的任何重复表达式。

(2)统计推导。在探索结束时,备忘录Memo将维护给定查询的完整逻辑空间。然后触发Orca的统计信息派生机制,以计算Memo组的统计信息。 Orca中的统计对象主要是列直方图的集合,用于得出基数和数据偏斜的估计值。统计信息是在紧凑的Memo结构上进行的,以避免扩展搜索空间。

为了导出目标组的统计信息,Orca会选择最有希望提供可靠统计信息的组表达式。统计承诺计算是特定于表达式的。例如,具有少量联接条件的InnerJoin表达式比具有大量联接条件的另一个等效的InnerJoin表达式更有希望(这种情况可能在生成多个联接顺序时出现)。基本原理是,加入条件的数量越多,估计误差的传播和放大机会就越大。由于需要在给定表达式的所有节点之间汇总置信度分数,因此计算基数估计的置信度分数具有挑战性。我们目前正在探索几种在紧凑的备忘录Memo结构中计算置信度得分的方法。

在选择了目标组中最有希望的组表达式之后,Orca递归地触发对所选择的组表达式的子组进行统计推导。最后,通过组合子组的统计对象来构造目标组的统计对象。 下图说明了正在运行的示例的统计信息派生机制。首先,执行一个自上而下的传递,其中父组表达式从其子组中请求统计信息。例如,(a = b)上的InnerJoin(T1,T2)请求T1.a和T2.b上的直方图。通过注册的MD提供程序按需从目录中加载请求的直方图,将其解析为DXL并存储在MD缓存中以为将来的请求提供服务。接下来,执行自下而上的遍历以将子统计对象合并为父统计对象。由于连接条件可能会影响列的直方图,因此会在列T1.a和T2.b上生成(可能已修改)直方图。

Statistics derivation mechanism

构造的统计对象附加到各个组,在优化过程中可以在其中进行增量更新(例如,通过添加新的直方图)。 这对于保持统计数据衍生成本的可管理性至关重要。

(3)实现。 触发创建逻辑表达式的物理实现的转换规则。 例如,触发Get2Scan规则以从逻辑Get中生成物理表Scan。 类似地,触发InnerJoin2HashJoin和InnerJoin2NLJoin规则以生成哈希和嵌套循环联接实现。

(4)优化。 在此步骤中,将强制实施属性,并为替代方案进行成本估算。 优化开始于向Memo的根组提交初始优化请求,以指定查询要求,例如结果分配和排序顺序。 向组g提交请求r对应于向g中的根物理运算符请求满足r的最低成本计划。

上图显示了备忘录中针对正在运行的示例的优化请求。 初始优化请求为req。 #1:{Singleton,},它指定需要根据T1.a给出的顺序将查询结果收集到主数据库。 我们还显示了组哈希表,其中每个请求都与以最低估计成本满足它的最佳组表达式(GExpr)相关联。 黑框表示插入到备忘录中的执行程序操作符,用于传递排序顺序和数据分发。 聚集运算符将所有段中的元组收集到主数据库。 GatherMerge运算符将所有段中的排序数据收集到主数据库,同时保持排序顺序。 重新分配运算符根据给定参数的哈希值跨段分配元组。

上图图显示了req的优化。 #1 by Inner-HashJoin [1,2]。 对于此请求,替代方案之一是根据联接条件对齐子分布,以便将要联接的元组放置在同一位置。 这是通过请求第1组的Hashed(T1.a)分布和第2组的Hashed(T2.b)分布来实现的。两个组均被要求传递任何排序顺序。 找到最佳子计划后,InnerHashJoin会合并子属性,以确定已交付的分发和排序顺序。 请注意,组2的最佳计划需要在T2.b上哈希分布T2,因为T2最初是散列在T2.a上,而组1的最佳计划是简单扫描,因为T1已经散列在T1.a.

2. 查询执行

最终计划的副本将分发到每个细分分段。 在分布式查询执行期间,每个段上的分布式强制执行器既充当数据的发送者,又充当数据的接收者。 例如,在段S上运行的Redistribute(T2.b)实例基于T2.b的哈希值将S上的元组发送到其他段,并且还从其他段上的其他Redistribute(T2.b)实例接收元组。

五、并行查询优化

查询优化可能是数据库系统中最占用CPU的过程。 有效使用CPU可以转化为更好的查询计划,从而提高系统性能。 并行查询优化器对于受益于利用越来越多的内核的高级CPU设计至关重要。

Orca是启用了多核的优化器。 优化过程被分解为称为优化任务的小型工作单元。 Orca目前有七种不同类型的优化工作:

  • Exp(g):生成组g中所有组表达式的逻辑等效表达式。
  • Exp(gexpr):生成组表达式gexpr的逻辑等效表达式。
  • Imp(g):生成组g中所有组表达式的实现。
  • Imp(gexpr):生成组表达式gexpr的实现替代方案。
  • Opt(g,req):返回计划的费用最少的计划,该计划是由操作员在g组中确定的,并且满足优化请求的要求。
  • Opt(gexpr,req):以gexpr为根并且满足优化请求req的估计成本最低的计划返回。
  • Xform(gexpr,t)使用规则t变换组表达式gexpr。

Optimization jobs dependency graph

对于给定的查询,可以创建每种类型的数百甚至数千个工作实例。 这引入了处理作业依赖性的挑战。 例如,只有在优化其子组之前,才能优化组表达式。 上图显示了部分作业图,其中优化请求req0下的组g0的优化触发了一棵深层的依赖作业。 依赖关系被编码为父子链接; 父级作业在子级作业完成之前无法完成。 当子级工作进展顺利时,父级工作需要暂停。 如果子作业不依赖其他作业,则允许它们选择可用线程并并行运行。 当所有子作业完成时,将通知已暂停的父作业以继续处理。

六、元数据交换

Orca旨在在数据库系统之外工作。优化器与数据库系统之间的主要交互点是元数据交换。例如,优化器可能需要知道是否在给定的表上定义了索引,以设计出有效的查询计划。元数据提供程序的集合促进了对元数据的访问,这些元数据提供程序是特定于系统的插件,用于从数据库系统检索元数据。

下图显示了Orca如何与不同的后端系统交换元数据。在查询优化过程中,Orca访问的所有元数据对象都固定在内存中的缓存中,并在优化完成或引发错误时取消固定。所有对元数据对象的访问都通过MD Accessor完成,该访问器可跟踪优化会话中正在访问的对象,并确保在不再需要它们时将其释放。如果请求的元数据对象尚未在缓存中,则MD Accessor还负责透明地从外部MD Provider提取元数据。服务于不同优化会话的不同MD访问器可能具有用于获取元数据的不同外部MD提供程序。

Metadata exchange framework

七、其它优化方案

1. 基础查询优化

Volcano Parallel Database 引入了实现数据库并行性的基本原理。 所提出的框架引入了交换运算符,该交换运算符实现了两种并行处理方式,即通过流水线实现操作符之间的并行化,以及通过跨运行在不同进程上的运算符对元组进行分区,实现了操作符内部并行化。 提出的设计允许每个操作符独立于本地数据执行,以及与在其他进程中运行的其他操作符并行工作。 几个MPP数据库()利用这些原理来构建商业上成功的产品。

Cascades 是可扩展的优化器框架,其原理已用于构建MS-SQL Server,SCOPE ,PDW 和Orca(我们在本文中介绍的优化器)。 该框架之所以流行,是因为它把逻辑和物理计划空间完全分开了。 这主要是通过将运算符和转换规则封装到独立的组件中来实现的。 这种模块化的设计使Cascades可以对逻辑上相等的表达式进行分组,以消除多余的工作,与Volcano的详尽的方法形成对比,允许按需触发规则,并允许根据规则的用途对给定的规则进行应用操作符排序。

2. MPP数据库SQL优化

存储和查询数据量的指数增长已转化为大规模并行处理(MPP)系统的更广泛使用,例如Teradata ,Oracle的Exadata ,Netezza ,Pivotal Greenplum数据库和Vertica 。由于篇幅所限,我们总结了最近在重新设计查询优化器以应对大数据挑战方面的一些努力。

SQL Server并行数据仓库(PDW)广泛使用了已建立的Microsoft SQL Server优化器。对于每个查询,PDW触发对SQL Server优化器的优化请求,该优化器在仅保留数据库的元数据和统计信息而不保留其用户数据的Shell数据库上工作。然后,将SQL Server优化程序采用的替代方案运送到PDW的数据移动服务(DMS),在其中使用分发信息对这些逻辑计划进行改进。尽管这种方法避免了从头开始构建优化器,但由于优化逻辑分布在两个不同的过程和代码库中,因此使调试和维护变得更加困难。

为并行执行而优化的结构化计算(SCOPE)是Microsoft的数据分析平台,它充分利用了并行数据库和MapReduce系统的特性。 SCOPE的脚本语言(例如Hive )是基于SQL的。 SCOPE是为使用仅附加文件系统的Cosmos分布式数据平台开发的,而Orca的设计目标是与多个基础数据管理系统一起工作。

SAP HANA 是一个分布式的内存数据库系统,用于处理业务分析和OLTP查询。 MPP数据库中的分析查询可能会产生大量中间结果。并发分析查询会耗尽可用内存,其中大部分已被消耗以存储和索引原始数据,并且会触发数据溢出到磁盘,从而对查询性能产生负面影响。

Vertica 是C-Store项目的商业化MPP版本,其中数据被组织为投影,每个投影都是表属性的子集。最初的StarOpt及其经过修改的StratifiedOpt优化器是为针对星型/雪花模式的查询而定制设计的,其中相同范围的联接键位于同一位置。如果无法实现数据并置,则可以在所有节点上复制相关的投影以提高性能,这是Vertica的V2Opt优化器所解决的。

3. SQL on Hadoop

在Hadoop上执行SQL的经典方法是使用Hive 将查询转换为MapReduce作业。 MapReduce的性能可能无法满足交互式分析的要求。 Stinger 是通过利用和扩展Hive在Hadoop上优化查询评估的一项计划。但是,这种方法可能需要对MapReduce计算框架进行重大的重新设计,以优化数据传递,并在磁盘上实现中间结果。

通过创建专门的查询引擎,无需使用MapReduce,就可以在HDFS中基于SQL的数据处理,从而解决了Hadoop上的交互式处理问题。 Impala ,HAWQ 和Presto是朝着这个方向努力的关键。这些方法的查询优化器和执行引擎的设计和功能不同,这两者都是查询性能的差异化因素。 DBMS和Hadoop技术的共同定位允许使用DBMS中的SQL和HDFS中的MapReduce在每个平台上本地处理数据。 Hadapt 率先提出了这种方法。微软还引入了PolyBase ,以提供将来自PDW 的表与HDFS上的数据连接起来的功能,以优化从一个平台到另一个平台的数据交换。

AsterixDB 是一种开放源代码的工作,可以基于NoSQL样式数据模型有效地存储,索引和查询半结构化信息。目前,AsterixDB的查询计划器是由用户提示驱动的,而不是像Orca这样的成本驱动的方法。 Dremel 是Google提供的可扩展的列存储解决方案,用于分析MapReduce管道的输出。 Dremel提供了类似于AsterixDB的脚本语言(AQL)和SCOPE 的高级脚本语言来处理只读嵌套数据。

八、总结

着Orca的发展,我们旨在开发一个查询优化平台,该平台不仅代表了最先进的技术,而且功能强大且可扩展,足以支持新优化技术和高级查询功能的快速开发。

在本文中,我们描述了完全从头开始构建这样的系统所需的工程工作。 集成到Orca中的许多技术保护措施带来了可观的投资,然而,以其快速的开发速度和由此带来的高质量软件的形式,已经获得了可观的收益。 Orca的模块化特性使它可以使用干净统一的抽象对系统功能和元数据进行编码,从而轻松地适应不同的数据管理系统。

本文内容来源于:Orca: A Modular Query Optimizer Architecture for Big Data

https://15721.courses.cs.cmu.edu/spring2016/papers/p337-soliman.pdf

代码:https://github.com/greenplum-db/gporca

 

责任编辑:赵宁宁 来源: 今日头条
相关推荐

2015-04-28 16:38:25

达梦

2019-08-28 16:18:39

JavaScriptJS前端

2018-06-21 09:36:09

模块化数据中心集中化

2016-12-21 17:02:35

数据中心MDC模块化

2021-10-11 09:51:37

模块化UPS架构

2010-08-18 09:03:07

模块化路由器

2010-05-13 17:36:50

2023-12-18 14:56:00

模块化单体系统数据库

2017-08-11 16:10:36

微信Android实践

2017-08-08 16:07:57

Android 模块化架构

2010-08-16 13:49:39

模块化路由器

2010-05-13 18:32:52

2017-01-10 13:48:24

华为大数据

2018-06-26 15:20:52

模块化数据中心UPS

2021-12-24 07:10:36

架构分层模块化

2017-07-11 11:02:03

APP模块化架构

2009-11-30 09:00:48

模块化路由器

2013-08-20 15:31:18

前端模块化

2017-05-18 10:23:55

模块化开发RequireJsJavascript

2020-09-17 10:30:21

前端模块化组件
点赞
收藏

51CTO技术栈公众号