本文总结了Impala在网易有数BI应用场景下的最新查询优化经验,并探讨后续进一步优化的思路。文章首先简述有数BI + Impala在网易云音乐等业务使用时遇到的挑战,再介绍进行有数查询优化的重要工具——网易Impala管理服务器,最后结合实际业务问题讨论具体优化方法及下一步计划。
01
有数BI + Impala遇上慢查询
在网易有数大数据平台架构中,有数商业智能(BI)产品提供了数据大屏、有数报告和自助取数等服务。
自助取数面向的用户是数据分析师,有数报告所面向的用户是BI工程师,允许用户直接通过拖拽UI界面的各种控件来获取所需的取数结果或数据报告,能够减少数据开发等相关工程师的工作量,大大提高分析师取数的效率和BI报告制作效率。
目前,有数产品在包括网易云音乐、严选、传媒等在内的网易集团内部业务,以及包括德邦快递、名创优品、温氏集团、古茗等外部客户的业务上均大规模使用。
世上武功,唯快不破。对于自助取数和有数报表,用户体验非常重要的一点就是要快。目前有数产品主要使用Impala作为数据查询引擎,相比Presto等其他开源OLAP查询引擎,Impala具有明显的性能优势。区别于社区版Apache Impala,有数使用的是网易大数据的Impala增强版。
在有数大规模使用中,音乐等业务场景也呈现出了有数+Impala的不少问题,包括查询错误较多、部分查询较慢等。针对这些问题,Impala内核小组与业务、大数据产品团队合作进行了大量优化,提高了查询成功率,减少了慢查询数量。
后面我们会结合案例详细分析如何优化。开始前,先介绍优化所用的2个工具:
在Impala这一侧,我们进行问题分析,寻找优化方法的主要工具是 Impala管理服务器,这部分在下一小节展开介绍;
另一个工具是有数报告,是的,我们用有数BI产品来对有数查询进行优化,将基于Impala管理服务器得到的分析结果制作成直观的图表报告。在优化过程也逐渐体会到有数产品的强大。
02
Impala管理服务器
Impala是个Hadoop生态下的MPP查询引擎,以性能著称,其核心组件包括Catalog,Statestore和Impalad,Impalad根据是否接收客户端查询请求又可分为coordinator/executor。Impala的系统架构如下所示:
目前社区版Impala在运维方面还存在不少短板。主要体现在如下方面:
社区版Impala为每个coordinator提供了webui界面,能够查看该coordinator正在执行和最近已完成的查询信息等。但Impala没有提供集群层面的查询视图,即没有将各coordinator节点的查询信息汇总到一个webui上。在观察集群查询状态时需要同时打开各个coordinator的webui并频繁切换;
coordinator的webui上展示的查询信息是非持久化的,一旦进程重启,这些信息就丢失了,而上线升级、系统bug等因素,进程重启是不可避免的;
coordinator的查询信息非持久化带来的另一个问题是:即使进程未重启过,其所能缓存的查询个数也是有限的,通过--query_log_size参数进行配置;
coordinator缓存的信息是在进程的地址空间内,未暴露出来。外部工具无法获取这些信息进行分析。
针对上述问题,网易大数据团队在Apache Impala社区版基础上开发了Impala集中式管理模块,即Impala管理服务器(managerd)。其主要功能包括:
汇总Impala集群中各coordinator节点正在执行和已完成的查询,提供统一的web端查询视图。这样在需要查找集群中正在执行或已完成的查询时,无需打开各coordinator的web界面;
将历史查询信息持久化到MySQL和对象存储上,有效防止进程重启或查询数量过多导致查询丢失问题。
管理服务器保存了集群上已执行的所有历史查询信息,包括查询基础信息表basic_info和查询明细表detail_info,如下所示:
detail_info相比basic_info的字段更少,但有多个mediumblob字段,其中包含了更丰富的查询信息。
detailinfo表有个profile字段,存储了NOS上的key,该key对应的NOS对象保存了完整的查询profile文件。
profile文件是深入分析查询整个执行过程的关键信息,包括查询的时间线(timeline),各个执行片段的counter信息、查询涉及的表是否有统计信息等。
03
使用痛点及优化
如前所述,我们可以把业务痛点分为慢查询和查询错误两类问题。下面就结合生产环境来举例说明具体存在哪些问题。
出现慢查询的原因很多,下面分别从Impala、有数BI产品和HDFS等维度来进行说明。
与主流的数据库和数仓查询引擎一样,Impala也是基于代价模型进行执行计划优化(CBO)。只有获取足够的统计信息,才能支撑Impala选取较优的执行计划。
但Impala作为一个查询引擎,往往不负责schema的创建和数据的导入,因此,也就无法在数据导入时计算统计信息。作为一个基于CBO的查询引擎,若用户不手动执行compute [incremental] stats计算统计信息,Impala的查询性能是要打折扣的。下图为统计缺失时的一个执行计划,可以看到531.35G的表分区作为右表被广播(broadcast)到集群的其他节点上进行join操作。
优化与改进
在对查询所涉及的表进行统计信息计算后,再次进行查询,join方式变为分区模式(partitioned)。
优化效果是显而易见的,除了性能得到了提升(从10分钟超时变为46s)。资源的消耗也急剧减低(详见04:hash join的mem-estimate值)。
因统计信息缺失导致的慢查询是普遍存在的,线上集群通过手动配置需要进行统计信息计算的表,对其跑compute stats脚本的方式来计算统计信息,作为临时的优化方式。
目前,我们已经依托Impala管理服务器开发了基于历史查询数据的自动统计信息计算功能,能够根据所配置的参数自动选择待处理的表,将其记录到compute_stats_info表中。
管理服务器通过后台线程读取这些表记录并进行统计信息计算。预计Q1上线使用。
除了基于CBO进行执行计划选择外,Impala通过将表元数据缓存在本地来提升查询性能,如将Hive表的元数据从Metastore(hms)加载到Catalogd和coordinator上,在为查询确定执行计划时就无需花时间通过RPC调用从hms获取所需的表元数据。
但由于Hive表会持续进行元数据更新,比如表分区的增加、删除和重命名,修改表属性等。这些都会导致Impala上缓存的元数据版本过旧,网易Impala版本增加了元数据同步功能,在hms侧有元数据更新时,会刷新(refresh table)或失效(invalidate metadata table)缓存的元数据。
问题原因及优化
元数据同步功能解决了元数据版本过低导致查询出错问题,但会失效缓存的元数据,导致性能下降。而且Hive表支持自定义属性,即用户可以增加一些具有特殊用途的表状态信息,比如网易大数据平台的元数据中心可为表增加访问次数等统计,举例如下:
'metahub.table.accessCount'='xxx', 'metahub.table.readCount'='xxx','metahub.table.readTimes'='xxx', 'metahub.table.referCount'='xxx',
元数据中心会调用hms的alter table接口以较高的频次更新这些信息(采样发现,在15分钟内多达191次)。但其实这些信息对于Impala并没有作用,不会对Impala执行计划产生影响。如前所述,alter table操作会触发Impala侧缓存失效,导致查询时需重新加载。
上图所示为一个总执行时间21.5秒的查询,其中11秒花在从hms加载表元数据上。
在大数据开发团队的支持下,已能够识别这些对Impala没有影响的alter table操作并将其过滤掉,从而提高查询的缓存命中率。
目前仍有较多必要的alter table操作场景会导致元数据失效,比如每天的离线数据产出。下一步,我们计划通过优化元数据更新的方式,及时收集因为各种原因导致的元数据缓存失效,通过后台线程将其重新加载到缓存中。
用户采用在有数界面拖拽控件的方式取数或制作图表,有数产品需要将其转换成Impala等查询引擎支持的SQL语句。有数生成的SQL是否合理,对查询性能具有重要的影响。下面列举SQL查询的优化案例。
时间/日期转换导致性能问题
就公司内部而已,事实表的数据一般以时间作为分区字段,如每天一个分区,分区字段类型为字符串。在分析报告中,经常需要将时间字段转换为时间戳类型,或进一步截取为分钟、小时、天、周、月等粒度。如下所示:
诸如此类,需要对每条记录都一一进行多个时间转换处理操作,势必会影响查询的性能。下面是个是否进行时间转换的查询性能对比。
大查询拖慢HDFS扫描性能
HDFS扫描性能往往会成为查询瓶颈,除了因与其他如离线分析等业务共用一套存储外,还有个原因是Impala下发了大量需扫描过多数据的查询语句。
有些业务表的单分区/天数据量超过500G,某些有数查询的分区范围指定过大或没有指定分区,比如1个季度或干脆不限定分区,则单个查询至少需扫描50+TB数据量。下图就是个案例。
更加严重的是,对于像有数这种BI报表产品,同个报告可能会包含多张相似的报表,类似的查询往往都是成批出现的,影响更大。
包括上述例子在内的多个SQL查询相关问题,在有数版本迭代过程中逐步得到了优化,比如下图为在有数7.3版本所做的2个优化。
该问题又可分为2种,分别是访问HDFS NameNode(NN)获取文件元信息和从DataNode(DN)读取文件数据。(需要注意的是,HDFS瓶颈是相对的,分场景的。对于Impala查询来说可能是瓶颈,但对于离线批处理任务来说,可能同样的性能表现并不构成瓶颈)。本文主要讨论DN相关问题及优化。
小文件问题 单文件过小,且文件数太多,导致无法通过顺序IO连续读取大数据块,需要重复走打开文件+读取数据的流程,效率较低;线上某些表存在较严重的小文件问题。如下所示例子,文件大小仅为10+MB,线上个别表的文件大小甚至仅为KB、Byte级别。
分区过大 一般情况下,数据扫描的最小粒度是分区级别,分区越大则需扫描的数据量大。下图所示某表,每天一分区,单分区文件数1k+,分区大小400+GB,共有200+分区。也就是说,如果需要分析1个星期数据,需扫描近3TB,若分析1个月,需扫约15TB。
表存储格式 SQL On Hadoop查询引擎查询Parquet或ORC格式的表文件时性能是最好的,如对Impala来说,对于Parquet或ORC格式,Runtime Filters(RF)特性的优势能够充分发挥,而对于TEXT文件格式,RF仅能作用在分区表上。下图所示为一张TEXT格式的100+G非分区表,该集群每日慢查询中有不小比例与该表相关。
对于DN相关的性能问题,涉及数仓治理,目前主要依赖业务的数仓团队配合基于实际的业务场景进行优化。对于TEXT表,建议业务尽可能修改为Parquet格式。
对于小文件问题。首先应该合理配置数据生产/导入并发度,尽量减少小文件的产出的机率;其次,对于已存在的小文件,应在分区内进行适当的合并;再次,对于每个分区的总数据量过小的情况,应该考虑不对表进行分区。
对于分区过大问题。可考虑进行数据清洗提质,去除其中无价值的数据。据了解,音乐有8亿+用户,一般情况,会计算分析每个用户相关行为或推荐数据生成事实表,但这其中有一定比例的用户是不活跃的,甚至是多年未登录的。对于某些用户相关事实表,可以考虑去掉不活跃用户的数据,从而减少每个分区内的数据量。
同时,大数据平台将为业务提供小文件合并和文件格式转换等一键式数仓优化功能。
业务在使用有数BI时会出现因为Impala侧查询错误导致取数结果或BI报告无法生成,严重影响有数产品的体验。业务反馈每天会有些查询出错,但不知道为什么出错,前端呈现的错误日志可读性差,因此也不知道该如何进行改进/优化。
我们根据basic_info表的state字段获取出错(exception)的查询,并结合detail_info表的status逐步整理出了不同原因导致的错误。下面列举出现较多的错误。
SQL自身错误
主要是SQL语法、参数限制和UDF误用等,举例如下:
org.apache.impala.common.AnalysisException: aggregate function not allowed in WHERE clause ...
org.apache.impala.common.AnalysisException: Exceeded the maximum number of child expressions (10000). ...
org.apache.impala.common.AnalysisException: No matching function with signature: default.dcount(BIGINT)
元数据错误
主要包括无法打开文件、列类型不兼容、Parquet格式不兼容、列未找到等,举例如下:
Disk I/O error on xxx.jd.163.org:22000: Failed to open HDFS file hdfs://hz-cluster11/user/da_music/hive/warehouse/xxx Error(2): No such file or directory ...
Error: File 'hdfs://hz-cluster11/user/da_music/hive/warehouse/xxx' has an incompatible Parquet schema for column 'xxx'. Column type: STRING, Parquet schema: ...
org.apache.impala.common.AnalysisException: Could not resolve column/field reference: 't2.current_card' ...
org.apache.impala.common.AnalysisException: Failed to load metadata for table: xxx
系统负载类错误
主要包括队列满、队列超时、SQL内存超值、进程内存超值等错误。
Rejected query from pool root.default: queue full, limit=160, numqueued=165 ...
Admission for query exceeded timeout 300000ms in pool root.default. Queued reason: queue is not empty (size 51); queued queries are executed first ...
Memory limit exceeded: Failed to allocate row batch EXCHANGENODE (id=31) could not allocate 16.00 KB without exceeding limit. ...
Failed to increase reservation by 68.00 MB because it would exceed the applicable reservation limit for the "Process"
查询被取消
错误提示很简单,有Cancelled、Session closed两种。均是产品侧主动kill了对应的Impala查询导致,可能原因有很多,我们目前主要关注因为执行时间超过阈值的查询,如音乐用的有数产品设置的阈值为10分钟,这些超时查询作为慢查询进行分析。
对于SQL自身错误,与上文所述SQL查询性能优化一样,主要与有数团队一起梳理对应的错误原因,改写有数SQL生成规则。
元数据错误
对于元数据错误,基本上是由于Impala元数据同步问题导致,举云音乐的有数EasyFetch集群为例。该集群在优化前存在较多因元数据同步导致的查询错误,以前的同学已初步定位到是由于Impala未同步通过“Impala同步”选项开启的表元数据,但并没有继续分析为什么会无法同步。
在本次优化期间,我们从头梳理了一遍“Impala同步”选项的功能和实现逻辑,确定是平台组件的代码bug导致,修复后此类错误大幅减少。
系统负载类错误
这是个综合性的问题,典型的场景是因为少数慢查询长期占用了系统资源。对于队列满、队列超时等错误,可以通过增加查询的并发数或排队超时时间来缓解,但提高查询并发数有可能会导致集群过载,查询性能进一步下降,反过来又会延长正在排队的查询的等待时间。另外一种可行的方式是直接向用户提示“当前系统负载过高,稍后再试”,避免用户在短时间内重复刷新页面导致情况恶化。
对于SQL内存或进程内存超值等错误,一般是由于复杂的大查询或对查询所需资源预估不准导致,对于前者,需要进行查询优化,比如减少数据扫描的范围等。对于后者,可通过补上表的统计信息来提高评估的精度。
系统负载类一般通过优化查询性能来解决。当然,如果一个集群每天都有好几个小时集中出现大量系统负载类错误,那么可以考虑是由于集群可用资源不够,应该及时扩容。
04
后续优化计划
经过大数据团队及业务的共同努力,网易云音乐、严选等业务的Impala集群在查询性能和错误收敛上取得了一定的成果,得到了音乐数仓团队的认可,达成了严选“双十一”确定的性能指标。
Impala的性能优化仍在继续,下面简单列举正在做的事情。
全面替换公司内部业务的Impala集群版本,从Impala 2.12升级到3.4版本,提供更强大的功能特性和性能表现。目前已完成音乐Impala集群升级;
引入Alluxio作为Impala与HDFS间的缓存层;
基于历史查询信息的表统计信息自动计算功能;
基于物化视图(临时表)的SQL重写功能,通过创建预聚合表来优化查询性能;
与有数产品团队合作实施有数查询诊断项目。