Impala3.4源码阅读笔记(五)统计信息

news2024/10/7 14:34:04

前言

本文为笔者个人阅读Apache Impala源码时的笔记,仅代表我个人对代码的理解,个人水平有限,文章可能存在理解错误、遗漏或者过时之处。如果有任何错误或者有更好的见解,欢迎指正。

基本信息

在Impala中,Stats记录了一张表的大小、行数等统计信息,这些信息存储在metastore中,并被Impala用来帮助优化查询,包括以下几点:

  • 准确的统计信息可以让Impala为JOIN查询构建高效的查询计划,提高性能并减少内存使用。
  • 准确的统计信息可以让Impala为Parquet表的INSERT更有效地分配负载,提高性能并减少内存使用。
  • 准确的统计信息有助于Impala估计每个查询所需的内存,这在使用资源管理特性时非常重要,如在准入控制和YARN资源管理框架中,统计信息帮助Impala实现高并发、内存充分利用,并避免与其他Hadoop组件的竞争资源。

由于许多最关键的性能和资源密集型操作依赖于表和列统计信息来构建准确和高效的执行计划,因此COMPUTE STATS是ETL过程末尾的一个重要步骤。

一、统计信息的查看

可以通过如下SQL语句查看统计信息:

-- 查看表级别统计信息
SHOW TABLE STATS [db_name.]table_name

-- 查看列级别统计信息
SHOW COLUMN STATS [db_name.]table_name

SHOW TABLE STATS和SHOW COLUMN STATS变量对于调优性能和诊断性能问题非常重要,特别是对于大表和复杂的JOIN查询。统计信息中任何未知的值(因为没有进行统计信息计算)都显示为-1。

二、统计信息的计算

可以通过如下SQL语句计算与删除统计信息:

-- 计算一张表的统计信息,可以指定只计算某些列,如果没有给出列列表,则计算表中所有列的列级统计信息
COMPUTE STATS [db_name.]table_name [ ( column_list ) ] [TABLESAMPLE SYSTEM(percentage) [REPEATABLE(seed)]]
column_list ::= column_name [ , column_name, ... ]

-- 增量计算统计信息,可以指定只计算某些分区,如果没有指定分区,则计算表中所有分区
COMPUTE INCREMENTAL STATS [db_name.]table_name [PARTITION (partition_spec)]
partition_spec ::= simple_partition_spec | complex_partition_spec
simple_partition_spec ::= partition_col=constant_value
complex_partition_spec ::= comparison_expression_on_partition_col

-- 删除统计信息
DROP STATS [database_name.]table_name

-- 删除增量统计信息,必须指定分区
DROP INCREMENTAL STATS [database_name.]table_name PARTITION (partition_spec)
partition_spec ::= partition_col=constant_value

可选的TABLESAMPLE子句指定COMPUTE STATS操作只处理指定百分比的表数据。对于太大而无法进行完整的COMPUTE STATS操作的表,可以使用带有TABLESAMPLE子句的COMPUTE STATS从表数据样本推断统计信息。PARTITION子句只允许与INCREMENTAL子句结合使用,且对于COMPUTE INCREMENTAL STATS是可选的,对于DROP INCREMENTAL STATS则是必需的。

执行COMPUTE STATS后Impala会自己执行另外一个SELECT查询来计算统计信息,如图所示:
在这里插入图片描述
需要注意的是,对于一张表,可以使用COMPUTE STATS或COMPUTE INCREMENTAL STATS,但不要混用或者交替使用两者。如果在一个表的生命周期内从COMPUTE STATS切换到COMPUTE INCREMENTAL STATS,或者反之,应当在切换之前先运行DROP STATS删除所有统计信息。

另外,第一次对一个表执行增量统计计算时,无论表是否已经有统计信息,统计信息都将从头开始重新计算,因此,当第一次在给定的表上运行COMPUTE INCREMENTAL STATS时,需要进行一次性的资源密集型操作来扫描整个表。

COMPUTE INCREMENTAL STATS只适用于分区表。如果对非分区表使用INCREMENTAL子句,Impala会自动使用COMPUTE STATS语句。这样的表在SHOW TABLE STATS输出的Incremental stats列下显示为false。

三、统计信息的检查

在开始制定查询执行计划时,在Frontend.java的createPlanExecInfo方法里会遍历所有本次查询需要Scan的表,逐一检查统计信息,以下是略去无关逻辑的部分代码:

// 缺失统计信息的表集合
Set<TTableName> tablesMissingStats = Sets.newTreeSet();
// 统计信息损坏的表集合
Set<TTableName> tablesWithCorruptStats = Sets.newTreeSet();
// 遍历每个ScanNode,ScanNode和本次查询要扫描的表一一对应
for (ScanNode scanNode: scanNodes) {
  TTableName tableName = scanNode.getTupleDesc().getTableName().toThrift();
  // 分别检查统计信息是否有缺失、损坏以及磁盘ID是否缺失,并将表名加入对应集合
  if (scanNode.isTableMissingStats()) tablesMissingStats.add(tableName);
  if (scanNode.hasCorruptTableStats()) tablesWithCorruptStats.add(tableName);
}

统计信息缺失

首先看统计信息缺失的检查,判断统计信息是否缺失使用了isTableMissingStats,可以发现统计信息缺失的判断包括两部分,分别是列统计信息的判断isTableMissingColumnStats和表统计信息的判断isTableMissingTableStats两者任一缺失,该表都被判断为缺失了统计信息,相关代码如下:

// 如果此扫描下的表缺少与此扫描节点相关的表状态或列状态,则返回true。
public boolean isTableMissingStats() {
  return isTableMissingColumnStats() || isTableMissingTableStats();
}

// 如果有列没有统计信息,则返回true,复杂类型的列将被跳过。
public boolean isTableMissingColumnStats() {
  for (SlotDescriptor slot: desc_.getSlots()) {
    if (slot.getColumn() != null && !slot.getStats().hasStats() &&
        !slot.getColumn().getType().isComplexType()) {
      return true;
    }
  }
  return false;
}

// ScanNode类的isTableMissingTableStats,如果表没有统计信息,则返回true
public boolean isTableMissingTableStats() {
  return desc_.getTable().getNumRows() == -1;
}

在列的统计信息检查isTableMissingColumnStats中可以发现其遍历了所有Slot,复杂类型的列(Struct、Map和Array)会被跳过。Slot包括具名和匿名两种,具名Slot可理解为SQL中SELECT的字段,匿名Slot为一些查询中间结果,如聚合运算。用Slot的getStats方法获取对应列的列统计信息ColumnStats并用其成员方法hasStats检查是否具有统计信息,也就是说列统计信息的检查只包括查询相关的列而非该表所有列,列统计信息由ColumnStats类定义,具体内容包括一列的空值个数、不同值个数、平均字节数和最大字节数。hasStats判断逻辑如下,即空值个数和不同值个数二者有其一已知(不为-1)即视为有列统计信息:

public boolean hasStats() { return numNulls_ != -1 || numDistinctValues_ != -1; }

表的统计信息由TTableStats定义,内容包括表的行数和文件大小。在isTableMissingTableStats中通过FeTablegetNumRows方法获取表行数,并判断该表的行数是否已知(即不为-1),已知表行数即认为该表有统计信息。HdfsScanNode类重写了isTableMissingTableStats方法:

// HdfsScanNode类重写的isTableMissingTableStats
@Override
public boolean isTableMissingTableStats() {
    // 使用了推测行数时不检查
    if (extrapolatedNumRows_ >= 0) return false;
    // NumClusteringCols即分区键的数量,该值大于0说明是分区表
    if (tbl_.getNumClusteringCols() > 0
        // numPartitionsWithNumRows_为有NumRows的分区数量
        // 若与要扫描的分区数量不等,则说明有分区缺失了统计信息
        && numPartitionsWithNumRows_ != partitions_.size()) {
        return true;
    }
    // 非分区表则调用父类方法,检查表级别的统计信息
    return super.isTableMissingTableStats();
}

可以发现对于分区表,表统计信息的检查只包括查询所涉及的分区而非该表所有分区,且任意一个涉及分区缺失统计信息都会判断为缺失表统计信息。

统计信息损坏

然后是统计信息损坏的情况,由hasCorruptTableStats检查,该方法在ScanNode中永远返回False,我们只看其在HdfsScanNode中的重写:

// 如果扫描的表被怀疑有损坏的统计信息,特别是当扫描为非空且numRows为0或负(但不是-1)时,返回true。
@Override
public boolean hasCorruptTableStats() { return hasCorruptTableStats_; }

// 指示是否有损坏的表统计信息,当ScanRange非空并且numRows为0时,设置为True。
private boolean hasCorruptTableStats_;

hasCorruptTableStats_为True时即统计信息损坏,该值主要在getStatsNumRows方法中被设置,以下为关键代码:

private long getStatsNumRows(TQueryOptions queryOptions) {
  ...
  hasCorruptTableStats_ = false;
  // 对于分区表,检查每个涉及的分区
  if (tbl_.getNumClusteringCols() > 0) {
    for (FeFsPartition p: partitions_) {
      long partNumRows = p.getNumRows();
      // 当该分区numRows为非-1的负数、或numRows为0而分区却有数据时,可以认为该分区统计信息损坏
      if (partNumRows < -1  || (partNumRows == 0 && p.getSize() > 0))  {
        hasCorruptTableStats_ = true;
      }
  ...
  // 当numRows为非-1的负数、或numRows为0而表却有数据时,可以认为统计信息损坏
  if (numRows < -1 || (numRows == 0 && tbl_.getTotalHdfsBytes() > 0)) {
    hasCorruptTableStats_ = true;
  }
  ...
}

总结一下,统计信息的损坏包括两种情况,一是numRows出现非-1负数,-1代表行数未知,非-1负数则是异常情况,二是numRows为0,表中却有数据,说明统计信息与表数据不匹配,这可能是表新增了数据,统计信息过时了。对于分区表,且任意一个查询涉及的分区统计信息损坏都会判断为表统计信息损坏。

无论统计信息缺失或者损坏都不会直接影响查询的执行,但是没有统计信息可能会影响查询性能,例如,缺失统计信息可能导致impala制定不合理的执行计划(如Join时广播大表),进而影响性能并占用更多资源。

当出现表统计信息缺失或损坏时,我们可以从查询的profile中得知,若有"Tables Missing Stats"字段则表示出现了统计信息缺失,并会在其后打印出表名。对于损坏的统计信息则是"Tables With Corrupt Table Stats"字段,其他同理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/720387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vuejs】1732- 详细聊一聊 Vue3 依赖注入

&#x1f449; 「相关文章」 深入浅出 Vue3 自定义指令详细聊一聊 Vue3 动态组件6 个你必须明白 Vue3 的 ref 和 reactive 问题初中级前端必须掌握的 10 个 Vue 优化技巧分享 15 个 Vue3 全家桶开发的避坑经验 在 Vue.js 中&#xff0c;依赖注入[1]&#xff08;DI&#xff09;是…

中小企业做知识管理如何选择KMS?

编者按&#xff1a;&#xff08;KM&#xff09;是创建、共享、使用和管理组织的知识和信息的过程。它是指通过充分利用知识来实现组织的多学科方法。那么中小企业预算有限的情况下&#xff0c;该如何选择KMS呢 &#xff1f; 关键词&#xff1a;知识管理系统、免安装、免维护 市…

在职读研弥补学历短板——中国人民大学与加拿大女王大学金融硕士项目

在当今社会 “文凭化”的理念下&#xff0c;学历变得很重要。学历会影响到一个人成长发展的各各方面&#xff0c;当我们“工作越久&#xff0c;接触社会越久”&#xff0c;越感觉到学历的重要性。具有高学历&#xff0c;就具有更多的发展机会&#xff0c;具有更多精神上的财富&…

Basler相机一丢包就断开问题解决

问题描述&#xff1a; 两个相机&#xff0c; 一个相机aca2500-14gm连接电脑主板100M网卡没问题&#xff0c;帧率3帧&#xff0c;但是不会断。 一个相机aca2500-14gm连接USB转网口&#xff08;千兆&#xff09;&#xff0c;pylon Viewer采图丢包严重并且几秒后相机断开。 解决…

集合面试题--复杂度分析

为什么要进行复杂度分析&#xff1f; 1指导编写出性能更优的代码2评判别人写的代码的好坏 时间复杂度分析 常见复杂度表示 常见复杂度 空间复杂度

【赠书活动 - 第1期】- 测试工程师Python开发实战(异步图书出品)| 文末送书

⭐️ 赠书 - 测试工程师Python开发实战&#xff08;异步图书出品&#xff09; 当初就是因为开发做不好&#xff0c;才去做测试了…… 这句玩笑话在过去可以说是测试人员的真实写照。 常规测试工作给人的印象&#xff0c;就是弄清楚软件功能&#xff0c;编写测试用例&#xff0…

基于springboot+Redis的前后端分离项目之消息队列(六)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 秒杀优化、消息队列 秒杀优化1 秒杀优化-异步秒杀思路2 秒杀优化-Redis完成秒杀资格判断3 秒杀优化-基于阻塞队列实现秒杀优化 Red…

抖音矩阵系统源码开源部署分享(三)

目录 一、 概述&#xff1a; 二、 账号矩阵搭建目的&#xff1a; 三、 抖音矩阵系统源码开发步骤 四、 功能规划 五、 代码开发展示 一、 概述&#xff1a; 抖音矩阵系统是指通过多个账号运营&#xff0c;对账号之间的内容和特征进行细分&#xff0c;账号之间相互引流推广&a…

什么是数字化和数字化转型?终于有人讲明白了!

在我与不同行业、不同岗位甚至不同阶层的人谈论数字化和数字化转型的时候发现一个很有意思的现象&#xff1a; 许多人出于无知或为了自己的利益而开始混淆这两个术语&#xff0c;甚至一些人已经开始将数字化标记为数字化转型&#xff0c;以安抚管理层、获得项目批准或进行销售…

在海外我们该如何推广应用

Google Play和Apple Store上有各种各样不同的应用程序&#xff0c;大量的正面评论和高评级可以成为应用在当前市场上取得成功的关键。大多数用户更喜欢有很多应用评论&#xff0c;积极反馈和高评级的应用程序&#xff0c;因此每条应用程序评论都很重要。确保鼓励用户留下评论&a…

R语言学习——数据框

x c(42,7,64,9) y1:4 z.df data.frame(INDEXy, VALUEx) z.df dim(z.df) # 查看几行几列 colnames(z.df) # 查看列名 rownames(z.df) # 查看行名 z.df[,1] z.df[1,] z.df[c(1,2),c(1,2)]df1 data.frame(C1c(1,5,14,1,54), C2c(9,15,85,9,42), C3c(8,7,42,8,16)) df1 df2 <…

力扣 78. 子集

题目来源&#xff1a;https://leetcode.cn/problems/subsets/description/ C题解1&#xff1a;递归回溯法。由于是求子集&#xff0c;所以根据nums.size()遍历每个子集的长度&#xff0c;并进行回溯。 class Solution { public:vector<vector<int>> res;vector<…

Blazor前后端框架Known-V1.2.2

V1.2.2 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 概述 基于C#和Blazor实现的快速开发框架&#xff0c;前后端分离&#xff0c;开箱即用。跨平台&#xff0c;单页应用&#xff…

scratch 篮球反弹

scratch 篮球反弹 本程序的功能是一个角色水平移动、碰到边缘反弹&#xff0c;“篮球”初始位置和方向随机&#xff0c;接触到其它角色或边缘时反弹。 具体内容如下 “篮球”角色 男孩角色

集成运放电路计算(全)

自记&#xff1a; 常用运放电路计算与分析 1、运放的符号表示 2、集成运算放大器的技术指标 (1) 开环差模电压放大倍数(开环增益)大 Ao(Ad)Vo/(V-V-)107-1012倍; (2) 共模抑制比高 KCMRR100db以上; (3) 输入电阻大 ri>1MW, 有的可达100MW以上; (4) 输出电阻小 ro 几W-几十…

如何将语音转换成文字?分享好用的3个方法!

为了方便制作会议记录&#xff0c;通常我们会录制会议内容&#xff0c;并在后期根据录音进行整理。然而&#xff0c;许多人在整理过程中感到痛苦&#xff0c;因为需要反复听取音频才能完成整理工作。其实&#xff0c;我们可以借助记灵在线工具将语音转换为文字&#xff0c;从而…

Rust 第一天---Rust环境配置

学习一门新的语言总是令人兴奋的,新的语法特性,设计理念…当然任何新的事物总是会留有旧事物的影子,这也能帮助我们更快地学习理解.作为2015年才正式发布的“年轻”语言,安全是它最大特性也是受欢迎原因之一.通过所有权系统进行内存管理,避免了其他高级语言因垃圾回收带来的消耗…

一百二十六、DBeaver——导入CSV文件(文件中无表字段)到ClickHouse

一、目标&#xff1a;将CSV文件的数据导入到ClickHouse中 备注&#xff1a;CSV文件没有表字段&#xff0c;只有纯粹的数据 二、实施步骤 第一步&#xff0c;右击表名&#xff0c;选择导入数据 第二步&#xff0c;在源类型和格式&#xff0c;选择从CSV文件导入&#xff0c;然…

企业如何建设积分商城?

企业建设一个成功的积分商城系统并不是一件简单的事情&#xff0c;需要注意诸多细节。我们该如何建设积分商城呢&#xff1f;作为一位电商行业十多年的从业者&#xff0c;这里分享一些经验。 一、明确商城建设目标 在建设积分商城之前&#xff0c;我们需要明确建设商城的目标&…