基于Impala的高性能数仓实践之执行引

导读:

本系列文章将结合实际开发和使用经验,聊聊可以从哪些方面对数仓查询引擎进行优化。

Impala是Cloudera开发和开源的数仓查询引擎,以性能优秀著称。除了ApacheImpala开源项目,业界知名的ApacheDoris和StarRocks、SelectDB项目也跟Impala有千丝万缕的联系。笔者所在的网易数帆大数据团队,是最早一批将其作为分析型数仓查询引擎的团队,目前正基于Impala打造有数高性能数仓引擎。

文章大致可以分为这几个部分:首先会对简单介绍下Impala的架构和元数据管理,以便后续内容展开;接着从执行引擎,存储优化,物化视图,数据缓存和虚拟数仓等维度进行探讨。本文为执行引擎篇。

1

Impala简介

Impala集群包含一个CatalogServer(Catalogd)、一个StatestoreServer(Statestored)和若干个ImpalaDaemon(Impalad)。Catalogd主要负责元数据的获取和DDL的执行,Statestored主要负责消息/元数据的广播,Impalad主要负责查询的接收和执行。

Impalad又可配置为coordinatoronly、executoronly或coordinatorandexecutor(默认)三种模式。Coordinator角色的Impalad负责查询的接收、计划生成、查询的调度等,Executor角色的Impalad负责数据的读取和计算。默认配置下每个Impalad既是Coordinator又是Executor。生产环境建议做好角色分离,即每个Impalad要么是Coordinator要么是Executor。

1.1元数据管理

Impala的元数据缓存在catalogd和各个Coordinator角色的Impalad中。Catalogd中的缓存是最新的,各个Coordinator都缓存的是Catalogd内元数据的一个复本。元数据由Catalogd向外部系统获取,并通过Statestored传播给各个Coordinator。

以Hive表为例,Catalogd中的元数据分别从HiveMetastore(HMS)和HDFSNameNode(NN)获取。从HMS获取的信息包括元数据信息和统计信息两部分,元数据信息指有哪些库和表,表定义,列类型等,对应“showdatabases,showtables,showcreatetablexxx,show”等操作。统计信息包括表的大小,行数,分区和各列的信息等,对应“showtablestatsxx,showcolumnstatsxx”等操作。从NN获取的是文件粒度的信息,包括文件存储位置,副本和文件块信息等。

1.2管理服务器

管理服务器是有数高性能数仓增加的Impala模块,提供集群粒度的SQL查看界面,持久化保存历史查询信息并展示,SQL审计,查询错误和查询性能分析,自动进行统计信息计算等。

2

执行引擎(ExecuteEngine)

2.1执行模型

在执行模型这块,目前主要有动态代码生成(codegeneration或justintime/JIT)和向量化计算两个流派,Impala主要是基于JIT进行性能优化,对于向量化引擎,Impala社区版目前并没有相关规划,有数高性能数仓团队也有计划对其进行向量化改造。

在具体实现上,Impala属于改进版的火山模型,官方论文描述为

TheexecutionmodelisthetraditionalVolcano?stylewithExchangeoperators.Processingisperformedbatch?at?a?time:eachGetNext()calloperatesoverbatchesofrows,similarto

即在传统的火山模型的基础上加入Exchange操作符,用于进行不同执行节点的数据交换。每次会获取一批记录而不是一条记录。

不管是JIT还是矢量化,其目的都是尽可能地减少执行引擎核心代码流程的调用次数并提高函数执行效率,这对于需要处理海量记录时非常重要。Impala通过每次获取一批记录来减少调用次数,再利用JIT技术来生成针对特定类型数据的执行流程函数,提高每次调用的效率。

更进一步,Impala采用数据流水线(streamingpipelined)执行机制,充分利用计算资源进行并发执行。在Impala4.0版本,完整支持了executor节点的多线程执行模型,进一步提高并发能力,压榨计算资源。

动态代码生成原理及优化

JIT技术与静态编译技术相反,其是在具体的查询运行之前才进行代码编译,此时,查询中需要处理的列类型,用到的算子和函数都已经确定,可以为该查询生成特定版本的处理函数。如下图所示:

左侧是通用的从文件读取记录(tuple)并解析的行数,外层一个for循环用于对每一列进行处理,内层的switch用于判断列的类型并调用特定的解析函数。如果我们已经知道该记录由三列组成,类型分别为int,bool和int,那么JIT技术就可以生成如图右侧的函数版本,不需要for循环,也不需要switch判断,显然,执行效率更高。

总的来说,Impala使用LLVM来进行JIT优化,生成对于某个具体查询最优的函数实现。其优化项具体包括移除条件分支(Removingconditionals,如上所示)、移除内存加载和内联虚函数调用等。

启用动态代码生成时,在查询执行前需要先动态生成其执行代码,因此有一定的时间消耗,对于小查询,动态代码生成可能是有害的,生成代码的时间都有可能超过SQL执行时间。Impala提供了DISABLE_CODEGEN_ROWS_THRESHOLD参数,默认为,如果SQL需要处理的记录数小于该值,则不会使用动态代码生成进行执行优化。Impala4.0版本对JIT进行了进一步优化,采用异步化改造来避免生成JIT代码对查询性能的影响,当编译未完成时使用原函数,完成后无缝切换成优化后的函数代码。

2.2计算资源

Impala属于SQLonHadoop的一种,基于MPP(MassivelyParallelProcessing,即大规模并行处理)架构,正常情况下,查询涉及的各种操作均在内存中完成的,因此,可用内存的多少及对其的利用效率,对Impala查询性能有极大影响。同样地,作为一个OLAP查询引擎,可用的CPU资源对查询性能也至关重要。Impala虽提供了少数CPU相关配置项,如num_threads_per_core等,但对CPU使用的控制能力较差。本小节后续仅介绍内存资源相关,CPU计算后续另开一篇单独介绍。

Impala资源池

Impala有比较丰富的资源使用限制方式,称为准入控制。其中资源池(resourcepool)是Impala进行并发控制的主要手段,可以决定某个查询是否会被拒绝,或执行,或排队。其主要有两种控制方式,一种是手动设置最大并发数控制,超过阈值的请求会进行排队,可以设置允许排队的最大请求数和排队时长,超过阈值的请求直接返回失败;另一种是基于内存的并发控制,下面进行重点介绍。

基于内存的并发控制

Impala集群支持通过fair-scheduler.xml设置多个资源池并规定其最大可用内存(maxResources),再通过llama-site.xml为每个资源池设置请求级别的内存限制,包括内存分配上下限max-query-mem-limit和min-query-mem-limit,及clamp-mem-limit-query-option。除了通过资源池相关配置控制请求的内存使用,还可以通过MEM_LIMIT请求选项设置内存限制。而clamp-mem-limit-query-option就是设置是否允许MEM_LIMIT设置的内存突破资源池内存配置的限制。

需要注意的是,max-query-mem-limit,min-query-mem-limit和MEM_LIMIT设置的是请求在每个executor节点允许申请的最大内存,请求申请的总内存还需要乘上执行该请求的executor节点个数。

若Impala通过预估发现查询所需的内存资源超过集群总内存资源,该查询会被拒绝;若总资源满足,但由于部分资源已被其他查询占用,则会将其放入请求队列,待可用资源满足查询要求时再按查询提交的先后顺序调度执行。

若预估的内存资源超过了设置的max-query-mem-limit,则以max-query-mem-limit为准,若小于min-query-mem-limit,则以min-query-mem-limit为准。假设查询请求设置了MEM_LIMIT,需先判断clamp-mem-limit-query-option的值,若为true,则仍然受max-query-mem-limit,min-query-mem-limit约束。下面举个例子进行说明:

假设一个Impala集群有5个executor节点,集群配置了一个最大可用内存为GB的资源池。查询请求的内存上下限为10GB和2GB,若clamp-mem-limit-query-option为true,Impala为某个查询请求A预估的内存为14GB(或设置了MEM_LIMIT为14GB),则查询A在每个executor最多只能分配10GB内存。若clamp-mem-limit-query-option为false,查询A最多可分配14GB内存。

假设clamp-mem-limit-query-option为true,则该资源池最多只能同时执行2个查询A这样的请求(2*5*10GB)。

通过上面的例子可知Impala的准入控制会在每个executor为查询请求预留所需的内存,因此,所预留的内存应该尽可能接近实际所需内存,预留过少会导致查询失败或中间结果溢出,预留过多会导致集群资源没有被充分利用。在内存资源管理的精确性方面,Impala还有较多需优化的点。

准入控制存在的问题

(1)集群同步

Impala进行准入控制的载体是coordinator节点,由于一个集群至少有2个及以上的coordinator节点,但准入控制是针对整个集群的。Impala通过statestore的impala-request-queuetopic机制在coordinator间周期性地同步每个coordinator上的查询并发和内存使用情况。

Impala采用去中心化的设计来实现准入控制,而不是通过一个中心节点来统一决策,虽然在性能和可用性上有优势,但是这会导致coordinator获取的其他coordinator信息过旧的问题,尤其是在查询并发度较高时,会导致准入控制模块做出错误的决策。

(2)内存预估精度

Impala需要基于统计信息来评估查询需要消耗多少内存,因为统计信息里面会记录表的记录数,列的类型和大小等。没有统计信息,就无法正确评估内存消耗,也就无法以较优的方式执行该查询。(统计信息相关的详细描述见下一小节)

但就算是有统计信息,仍有可能依然没法正确估算需消耗的内存量。如下所示:

上图第一张的"MemUsage"和"MemEstimate"分别表示查询实际消耗和预估消耗的总内存,可见明显差别。上图下面两张为通过


转载请注明:http://www.aierlanlan.com/rzdk/877.html

  • 上一篇文章:
  •   
  • 下一篇文章: