Impala笔记
体系结构
Impala 是一个分布式并行计算数据库引擎,MPP架构

Impala Daemon
Impala 的核心组件是 Impala Daemon,在物理上表现为 impalad 进程
(1)核心功能
在 CDH 5.12/ Impala 2.9 之后的版本,支持将 coordinator 和 executor 分布在不同的机器上,每个 impalad 可专注于执行不同类型的任务,以提高大集群上高并发任务负载的可扩展性。
coordinator角色
- 接收通过各种方式传输来的查询,包括 impala-shell,Hue,JDBC,ODBC
- 获取元数据并缓存,生成物理执行计划
- 将查询任务分发到executor并行执行
executor角色
- 读取外部数据
- 不需要缓存元数据,executor将中间查询结果传输回 coordinator
一节点双角色会产生如下扩展性问题:
- 扮演coordinator角色会影响其执行具体查询的能力,当查询包含大量分片时coordinator会出现显著的网络和cpu开销,每个coordinator需要缓存所有表和分区的元数据,本来这些内存可用于执行joins、aggregations
- 大量节点扮演coordinator会产生不必要的网络开销,且由于每个coordinator都要从statestored获取metadata update,甚至会同步超时
(2)实现
前端和后端分开实现,前端被嵌到了后端程序里,共享相同的进程ID
- 后端由C++开发,用来执行查询
- 前端由java开发,用来编译执行计划、存储元数据
Impala StateStore
- 检测 Impala daemon 的健康状况,集群中只能有一个 statestore,物理表现为 statestored,当 daemon 状态异常时,statestore 会通知其他 daemon 不再发送查询到此 daemon 。
- impalad coordinator与catalogd间进行元数据同步要通过statestore进行转发
Impala Catalog
- catalog 负责接受Impalad coordinator的元数据请求,并与HMS、NameNode交互,加载最新的元数据,进程名为 catalogd,一个集群只需要一个 catalogd,由于请求需要经过 StateStore,所以在同一台机器上运行 catalogd 和 statestored 比较科学。
- 当元数据发生变化时向所有Impalad coordinator广播发生了元数据变化,catalogd 的存在避免了每次由于 Impala 语句导致的元数据变化时都要执行 REFRESH 或者 INVALIDATE METADATA。不过,如果你通过 Hive 进行建表或加载数据等操作时,还是需要在查询前执行 REFRESH 和 INVALIDATE METADATA,不过只需要在一个 Impala Daemon 上执行。
Impala Metastore 和 Hive metastore 的关系
Impala 除了维护 Hive Metastore 中记录的信息之外,还会跟踪数据文件的元数据,即 HDFS文件的物理位置。
缓存管理
Impala使用两个元数据:
- 来自Hive Metastore的目录信息
- 来自NameNode的文件元数据
在通过非Impala操作修改数据时(如:Hive操作、Spark操作Hive、HDFS直接操作表文件等),Impala是无感知的,此时需要在Impala端进行手动刷新元数据
多个impalad同时提供服务,并且它会由catalogd缓存全部元数据,再通过statestored完成每一次的元数据的更新到impalad节点
通过其他手段更新元数据或者数据对于Impala是无感知的,例如通过hive建表,直接拷贝新的数据到HDFS上等,Impala提供了两种机制来实现元数据的更新,分别是INVALIDATE METADATA和REFRESH操作
1、invalidate metadata
刷新全库或者某个表的元数据,包括表的元数据和表内的文件数据,它会首先清除表的缓存,然后从metastore中重新加载全部数据并缓存,该操作代价比较重
--只更新某表
INVALIDATE METADATA luoge_nsh_mid.mid_role_portrait_all_d;
--更新全库
INVALIDATE METADATA;
2、refresh
刷新某个表或者某个分区的数据信息,它会重用之前的表元数据,不拉取元数据,仅仅执行文件刷新操作,它能够检测到表中分区的增加和减少,主要用于表中元数据未修改,数据的修改
3、使用原则
(1)禁止使用 invalidate metadata,这条语句会刷新全集群的元数据,相当于重启 catalogd,开销很大
(2)缩小刷新元数据的粒度,比如一个表只是某分区发生变化,使用 refresh [table] partition [partition]
(3)如果只涉及数据改变,使用 refresh 而不是 invalidate,refresh 会重用之前的元数据,仅执行文件刷新操作,invalidate 是个更重量级的刷新操作,首先清空表缓存,然后重新从 metastore 拉取元数据,加载文件数据
(4)如果涉及元数据改变,使用 invalidate [table]
(5)在 dml 操作完成后执行一次刷新,而非每次查询前都刷新
refresh只会对执行这个命令的节点能即时生效,其他节点需要一些时间同步
connect_impala(impala_ip, impala_port, "insert into default.t2000 partition(ds='2020') values (10)") connect_impala2(impala_ip, impala_port, "select * from default.t2000") connect_impala(impala_ip, impala_port, "refresh default.t2000") connect_impala2(impala_ip, impala_port, "select * from default.t2000") connect_impala2(impala_ip, impala_port, "select * from default.t2000")

refresh或invalidate操作过程中不会同步将元数据更新到所有节点上,而是此节点先通过statestore请求catalogd,然后请求者优化更新,然后会异步更新到所有节点。因为使用leastconn haproxy负载均衡策略,每次执行命令的节点基本都是不一样的,这次在这个节点refresh,在另一个节点执行另一命令时识别不到的元数据
经过反复验证,之所以之前不需要refresh而现在需要refresh,是因为之前相当于是单节点运行的,一个节点做的更改很快就能反映在自身的元数据里,相当于即时更新的。如果要更新到所有的节点,需要一定时间,如果不手动refresh会使别的节点无法感知到变化,现在需要refresh就是因为更新策略后,不同sql是在不同节点运行的,变动元数据后,其他节点无法即时感知到
比如顺序运行这一串sql
connect_impala(impala_ip, impala_port, "insert into default.t2000 partition(ds='2020') values (10)") connect_impala2(impala_ip, impala_port, "select * from default.t2000") connect_impala(impala_ip, impala_port, "alter table default.t2000 drop partition (ds='2020')") connect_impala(impala_ip, impala_port, "compute stats default.t2000") connect_impala2(impala_ip, impala_port, "select * from default.t2000")
使用leastconn策略的端口,分布式执行

使用source策略的端口,单节点执行

还有另一种解决方法,就是不要每执行一个sql就建一个连接,而是一个连接执行多条sql,这样其实就用的是同一台机器
import impala.dbapi impala_ip = 'fuxi-luoge-01' impala_port = 36011 conn = impala.dbapi.connect(host=impala_ip, port=impala_port, auth_mechanism='GSSAPI',kerberos_service_name='impala', database="default") cursor = conn.cursor() cursor.execute("insert into default.t2000 partition(ds='2020') values (10);") cursor.execute("alter table default.t2000 drop partition (ds='2020')") cursor.execute("insert into default.t2000 partition(ds='2020') values (10);") cursor.execute("alter table default.t2000 drop partition (ds='2020')") cursor.execute("insert into default.t2000 partition(ds='2020') values (10);") cursor.execute("alter table default.t2000 drop partition (ds='2020')")
COMPUTE STATS
收集卷信息, 表列和分区的数据分布情况,存储在元数据库中,用于构建精确和有效的查询计划,优化Impala查询
COMPUTE INCREMENTAL STATS luoge_nsh_mid.mid_role_portrait_all_d PARTITION (ds='2019-03-05');
COMPUTE STATS t1;
负载均衡
使用haproxy,Hue客户端balance选择source,非Hue客户端balance选择leastconn
端口
krpc_port 27000
catalog_service_port 26000
webserver_port 25000
beeswax_port 21000 Impala Daemon Beeswax 端口 impalashell
hs2_port 21050 Impala Daemon HiveServer2 端口 jdbc,比如beeline
state_store_subscriber_port 23000
be_port 22000 Impala 后台程序后端端口
参数
1、--load_catalog_in_background
禁止Catalog自动加载 metadata, 这可以有效减少catalog更新数据, 从而延缓节点通讯的问题。从IMPRA 2.2开始,对于load_catalog_in_background的默认值是false,与自动更新元数据没关系,只是加速元数据加载
在CDH5.3之前的版本中, Statestore通过heartbeat来发送catalog update的消息,这对于规模较小的 metadata问题不大,因为此时catalog update 通常不会很大,不至于影响到传送heartbeat。但随着数据量不断增多(文件和partition数量增长),catalog update 也变大,此时,传输延迟就会影响到heartbeat,导致 Impalad 认为 Statestore 不可达,从而尝试重新连接注册。而如果主机数量很多,就会进一步扩大影响,造成不断有Impala重新注册,从而造成 Statestore 内部 queue 溢出,最终出现Impalad卡在了注册Statestore上。
2、自动更新元数据
cdh 6.3提供,需要配置hive、impala,相当于hive事件驱动的模式。cdh 6.3前hive建的表必须手动同步到impala,但impala建的表可以在当前impalad节点即时看到,在其他节点会晚一些看到
3、impalad内存管理
前端和后端的内存占用是独立的,两者相加不可大于物理内存
后端c++

前端java
默认使用四分之一的主机物理内存或32G
impalad:
Cloudera Manager Home Page > Impala > Configuration > Impala Daemon Environment Advanced Configuration Snippet (Safety Valve)
JAVA_TOOL_OPTIONS=-Xmx?g -XX:+PrintGC
catalogd:
Impala Catalog Server Environment Advanced Configuration Snippet (Safety Valve)
看效果:
sudo -u impala jcmd $(pgrep -f IMPALAD) VM.flags
sudo -u impala jcmd $(pgrep -f CATALOG) VM.flags
4、catalogd内存管理

5、set sync_ddl=1
确保impalad各节点的元数据更新在执行结果返回前已经完成
impala-shell -i load-balancer-host -q "SET SYNC_DDL=1; CREATE TABLE test (a INT)"
impala-shell -i load-balancer-host -q "SELECT * FROM test"
6、--use_local_tz_for_unix_timestamp_conversions
impala unix_timestamp()生成的unix时间戳会比hive多8小时,即28800
这是因为impala默认会将字符串时间视为utc时间,然后自动转换到本地时区(东8区)时加8个小时。而hive一般会设置时区到东8区,此时会将东8区的时间转换成东8区的时间戳,也就不需要加8小时(unix时间戳本来是不存时区信息的,需要在外部加以定义)
select unix_timestamp('1970-01-01 00:00:00')


解决方法:
操作
cm面板 -> impala -> 配置 -> 搜索[Impala Daemon 命令行参数高级配置代码段(安全阀)], 填写【use_local_tz_for_unix_timestamp_conversions=true】
保存 -> 重启impala
7、检查当前配置
You can check the current runtime value of all these settings through the Impala web interface,
available by default at http://impala_hostname:25000/varz for the impalad
daemon, http://impala_hostname:25010/varz for the statestored daemon, or
http://impala_hostname:25020/varz for the catalogd daemon.
8、queue_wait_timeout_ms
Admission Control队列超时 Maximum amount of time (in milliseconds) that a request will wait to be admitted before timing out.
query_timeout_s and session_timeout_s
use these properties to set timeouts for query execution and for sessions in Hue, which causes queries and sessions to be cancelled when the timeout period expires. They are set in seconds
9、compact_catalog_topic
通过statestored传输catalog update前,压缩topic size,需要在catalogd、statestored、所有impalad设置为true
10、is_executor、is_coordinator
默认都为true,即每个impalad既是coordinator又是executor,
一节点双角色会产生如下扩展性问题:
- 扮演coordinator角色会影响其执行具体查询的能力,当查询包含大量分片时coordinator会出现显著的网络和cpu开销,每个coordinator需要缓存所有表和分区的元数据,本来这些内存可用于执行joins、aggregations等
- 大量节点扮演coordinator会产生不必要的网络开销,每个coordinator都要从statestored获取metadata update
分工现有节点可以减小coordinator数量,降低statestore负担,提升metastore同步和executor执行效率
分工时注意:
- 网络、内存负载主要集中于coordinator,保持metadata时刻更新,路由用户查询到正确的节点。一般情况下,小于10节点的集群只要1个coordinator就够了,如果cpu负载达到80%以上,按1个coordinator对应50个executor增加。应该放到一个空闲节点,需要不少于executor的内存
- cpu、内存负载主要集中于executor,应该放到DataNode节点
注意,当分配角色后,客户端只能通过coordinator节点进行访问



11、kerberos_reinit_interval
impalad发送认证请求的时间间隔,默认60分钟,在大集群可以加到6小时
运行时代码生成技术 LLVM
可以提升执行性能,如果没有运行时的代码生成,为了处理编译时未知的运行时数据,函数中总是会包含低效的代码。例如,仅仅处理整数的记录解析函数,在处理只有整数的文件时,会比处理各种数据类型的通用函数要快得多。然而要扫描的文件schema在编译时是未知的,所以这种通用的函数尽管低效,却也是必要的。但JIT与这种思路完全相反,函数在运行时被完全编译成对当前数据最高效的写法。这在我们平时看来甚至都不能算作函数,因为完全不通用,逻辑都用常量固定写死了,但这正是JIT的策略
LLVM是一个编译器及相关工具的库,不同于独立应用式的传统编译器,LLVM是模块化且可重用的,允许Impala等应用在运行的进程内执行JIT(just-in-time)编译

常用的优化技术:
- 移除条件分支:因为已知运行时信息,所以可以优化if/switch语句。这是最简单有效的方式,因为最终机器码中的分支指令会阻止指令的管道化(instruction pipelining)和并行执行(instruction-level parallelism)。同时,通过展开for循环(因为我们已经知道循环次数)和解析数据类型,分支指令能被一起移除。
- 移除内存加载:从内存加载数据是开销很大而且阻止管道化的操作。如果每次加载的结果都一样的话,我们就可以使用代码生成来替代数据加载。例如,之前图1中的数组offsets_和types_在每次查询开始时创建而不会改变,于是在代码生成的函数版本中,展开for循环后,这些数组中的值可以直接内联化。
- 内联虚函数调用:虚函数对性能的影响很大,尤其是函数很小很简单,因为它无法内联化。因此当对象实例的类型在运行时可知时,我们可以使用代码生成来取代虚函数的调用,并做内联化。这对于表达式树的求值尤为有价值。在Impala中,表达式由操作和函数的树组成,例如下图。树中出现的每种表达式都是覆盖(override)表达式基类的函数来实现的,基类会递归地调用各个子表达式。许多表达式函数都是非常简单的,例如两数相加,于是虚函数调用的开销甚至大过表达式求值的开销。通过代码生成移除虚函数并内联化,表达式可以无需函数调用而直接求值。此外,内联后的函数使编译器做进一步的优化,例如子表达式消除等


浙公网安备 33010602011771号