Impala元数据性能改善(3.3版本)
本文介绍了Impala在3.3版本对元数据性能方面做的一些优化和改善,主要结合官方的文档和测试结果进行说明。
按需获取元数据
在之前的Impala版本中,每个coordinator都会在自己的内存中保存一份catalogd的全量元数据缓存,而这会消耗很大的内存,并且这些元数据缓存都会一直保存在coordinator中。 元数据信息是通过statestored服务来进行传播的,并且是顺序加载的,例如一个用户加载一个大表,会阻塞另一个用户加载一个小表。
使用该新功能,coordinator可以根据需要从catalogd中获取元数据,并将其缓存到本地,并且缓存的元数据也会在内存使用过高的情况下自动失效。
目前,按需获取元数据功能在coordinator和catalogd之间的传播粒度为分区级别。常见的像add/drop partition操作,不会引起不必要的元数据的序列化/反序列化。
按需获取元数据功能目前主要涉及到两个参数项配置:catalog_topic_mode需要配置在catalogd中;use_local_catalog需要配置在coordinator。根据catalog_topic_mode配置的不同,会有如下几种模式:
按需获取元数据模式
–catalog_topic_mode=minimal
–use_local_catalog=true
混合模式
–catalog_topic_mode=mixed
对于需要开启按需获取元数据功能的coordinator节点配置:
–use_local_catalog=true
不需要开启此功能的配置:
–use_local_catalog=false
当我们开启了该功能之后,coordinator端的元数据缓存称之为LocalCatalog,相关的代码实现位于CatalogdMetaProvider.java文件中。其中缓存使用的是goole Guava提供的缓存库我们可以在coordinator配置如下的两个参数:
- local_catalog_cache_mb,表示元数据缓存可以使用的大小,单位是mb,默认-1表示可以使用的大小为jvm heap的60%;
- local_catalog_cache_expiration_s,元数据缓存的过期时间,单位是秒,默认是3600s。
值得注意的是,如果开启了该功能,那么在coordinator的web页面,无法看到库/表的详细元数据信息,只有简单的库名和表名:
请注意,启用按需获取元数据功能后,不支持全局的INVALIDATE操作。
相关JIRA:IMPALA-7935、IMPALA-7469、IMPALA-7447、IMPALA-7137
注:由于该功能涉及到的改动比较多,因此这里列举出的JIRA可能不完整,请见谅。
元数据缓存自动失效
在3.1版本中,Impala推出了元数据缓存自动失效功能,该功能可以限制元数据的大小,catalogd会定期扫描所有的表,并将最近未使用的表标记为失效状态。主要有以下两种策略:
基于时间的元数据缓存失效
通过在impalad和catalogd中配置invalidate_tables_timeout_s(单位是秒),如果表在该指定的时间段内没有使用,则catalogd会将该表的元数据缓存置为失效(即对表执行invalidate操作)
基于内存的元数据缓存失效
基于内存的元数据缓存失效策略有以下三个参数需要了解:
- invalidate_tables_on_memory_pressure,默认为false,true表示开启基于内存的元数据缓存失效功能;
- invalidate_tables_gc_old_gen_full_threshold,默认为0.6,表示经过一次GC之后,如果老年代的使用比例超过60%,则将最近使用最少的表的元数据缓存置为失效;
- invalidate_tables_fraction_on_memory_pressure,默认为0.1,表示要处理10%的表,将它们的元数据置为失效;
元数据缓存自动失效功能,通过在表中增加了一个最后访问时间lastUsedTime_,并且额外启动一个线程来不断扫描是否有表满足了以上的两个策略对应的条件,如果满足的话,则主动调用invalidateTable方法来使表的元数据缓存失效。注意,在使用元数据缓存自动失效功能的时候,最好将load_catalog_in_background参数设置为false,否则可能会影响该功能的使用。
元数据缓存自动失效功能,对于表很多,但是没有及时清理的场景非常合适,可以控制元数据缓存的大小,将不用的表及时从catalogd中去掉。但是也存在以下的问题:
- 如果是单表分区数很多(十几万、几十万级别),该功能则无法起到减少元数据缓存的效果。因为从代码可以看到,该功能的粒度是表级别的,每次都是将整个表的元数据缓存置为失效;
- 对于基于内存的元数据缓存失效策略,如果内存使用达到阀值,但是表都是短期内需要使用的,那就可能会出现频繁的元数据加载/失效的情况,这样反而可能会导致性能的下降,需要注意。
相关JIRA:IMPALA-7448
自动invalidate/refresh元数据
在先前的Impala版本中,如果使用Hive/Spark进行了DDL/DML操作,例如create/drop,alter table add/drop partition等,Impala是无法主动感知这种变化的,需要我们手动提交invalidate metadata/refresh xxx命令。
在Impala的最新版本中,提供了对于元数据的自动invalidate/refresh。在启动该功能之后,通过配置hms_event_polling_interval_s参数项(默认为0,设置为正整数表示正常的轮训频率,官方建议设置小于5s),catalogd可以以指定的间隔轮询HMS的通知事件,并处理以下的变更操作:
注意:这是Impala 3.3中的预览功能,通常不可用。
- 当收到ALTER TABLE事件时,invalidate表;
- 当收到ALTER, ADD, or DROP partitions事件时,refresh分区;
- 当收到CREATE TABLE or CREATE DATABASE事件时,增加库/表到catalogd;
- 当收到DROP TABLE or DROP DATABASE事件时,从catalogd中移除对应的元数据缓存;
- 当收到INSERT事件时,refresh表和分区;
如果在处理INSERT事件时并没有加载表,则事件处理器不需要refresh表,只是跳过它。 - 当收到ALTER DATABASE事件时,会更改数据库,catalog也会更新信息。以下的变更是支持更新的,但是不会invalidate库中的表。
- 修改数据库的属性
- 修改数据库的注释
- 修改数据库的owner
- 修改数据库的默认存储位置
更改数据库的默认位置不会将数据库的表移到新位置。只有在这之后新建的表才使用数据库新的默认位置。
以下情况目前是不支持的:
- 绕开HMS,通过直接在文件系统上添加文件来将数据添加到表中或从表中删除数据时,HMS不会生成INSERT事件,事件处理器也不会invalidate表或者refresh相应的分区。在这种情况下,
建议使用LOAD DATA命令来显示加载数据,以便事件处理器可以对LOAD命令生成的事件进行相应的操作。 - Spark API将数据保存到指定的路径,这种情况下HMS也不会生成事件,例如:
Seq((1, 2)).toDF("i", "j").write.save("/user/hive/warehouse/spark_etl.db/customers/date=01012019")
为基于事件的自动元数据同步配置HMS
为了使用上面提到的元数据自动invalidate/refresh功能,我们需要在HMS中进行相应的配置,操作如下:
- 在HMS服务的hive-site.xml中增加如下配置项:
<property>
<name>hive.metastore.transactional.event.listeners</name>
<value>org.apache.hive.hcatalog.listener.DbNotificationListener</value>
</property>
<property>
<name>hive.metastore.dml.events</name>
<value>true</true>
</property>
- 保存hive-site.xml;
- 在HiveServer2服务的hive-site.xml中配置hive.metastore.dml.events,设置为true。请注意,这个参数在HiveServer2和HMS服务中都需要配置的;
- 如果Spark服务也要使用该功能的话,同样需要在Spark服务使用的hive-site.xml中配置hive.metastore.dml.events为true,这样当Spark往插入现有表和分区插入数据时,就会生成相应的INSERT事件;
- 重启HiveServer2、HMS和Spark(如果使用的话)服务。
目前,最新的Impala源码提供的mini cluster环境已经可以对该功能进行验证,在相应的测试HMS和HiveServer2服务对应的hive-site.xml中,我们可以看到上面提到的配置项(配置文件位于$IMPALA_HOME/fe/src/test/resources/)。当mini cluster都启动之后,我们可以通过beeline或者hive client创建测试表,然后连接至impala集群,查询新建的测试表是否已经被同步到impala。
禁用基于事件的自动元数据同步
将catalogd的hms_event_polling_interval_s参数设置为非0值之后,自动元数据同步功能就会对所有的库和表生效。如果希望对哪个库/表进行同步有更细粒度的控制,可以通过在库/表级别设置impala.disableHmsSync属性来禁用事件处理。
在DBPROPERTIES或者TBLPROPERTIES中,通过设置impala.disableHmsSync可以控制自动元数据同步功能的开关。impala.disableHmsSync这个属性的值,会决定特定的库/表是否会禁用事件处理。
- 如果impala.disableHmsSync为true,则库/表的事件会被忽略掉,不会同步到HMS;
- 如果impala.disableHmsSync为false,或者没有配置,则表示开启自动同步功能;
- 如果新建的库不要开启自动同步功能,则需要在Hive中使用如下的DDL语句,Impala目前不支持设置库属性:
CREATE DATABASE <name> WITH DBPROPERTIES ('impala.disableHmsSync'='true');
- 开启或者禁用表的自动同步功能,使用如下的语句:
CREATE TABLE <name> WITH TBLPROPERTIES ('impala.disableHmsSync'='true' | 'false');
- 要改变表的自动同步功能开关,则使用如下的语句:
ALTER TABLE <name> WITH TBLPROPERTIES ('impala.disableHmsSync'='true' | 'false');
如果同时设置了库和表的属性,则表级别的属性优先考虑。如果表级别的属性未设置,那么库级别的属性会进行考虑。
如果属性从true(表示跳过事件处理)改成了false(表示不跳过事件处理),则需要通过手动执行invalidate metadata来重置事件处理。因为事件处理器并不知道之前跳过了多少事件,也无法确定当前事件中的对象是否为最新的(个人对这段话的理解是,最开始为true的时候,事件处理器会一直跳过event,即使是设置false这个事件可能也会被跳过,所以需要手动INVALIDATE)。在这种情况下,事件处理器的状态会变成NEEDS_INVALIDATE。
同样,我们通过mini cluster可以对该功能进行验证,当我们使用hive client执行如下两条命令的时候:
CREATE TABLE student_test_01(id int) TBLPROPERTIES ('impala.disableHmsSync'='true');
CREATE TABLE student_test_02(id int) TBLPROPERTIES ('impala.disableHmsSync'='false');
通过impala shell连接查看,会发现student_test_01已经被同步,但是student_test_02没有被同步。
基于事件的自动元数据同步的metrics
可以通过catalogd的webui(默认是http://hostname:25020)页面来查看事件处理器的状态。在WebUI有两个页面是跟自动元数据同步相关的,分别是:
- /metrics#events
- /events
这个页面提供了事件处理器metrics的详细视图,包括/metrics#events页面上列出的所有计数器的持续时间和速率指标的最小值,最大值,平均值,中位数。
参考文档
推荐阅读