Spark SQL 性能优化再进一步 CBO 基于代价的优化

本文详细介绍了Spark SQL中的CBO(基于代价的优化)原理,包括统计数据收集、算子对数据集影响的估计和代价估计。CBO通过考虑数据特性和操作代价,选择最优的物理执行计划。文章还探讨了Build侧选择、Join类型优化和多表Join顺序优化,揭示了CBO如何提升查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原创文章,转载请务必将下面这段话置于文章开头处。
本文转发自技术世界原文链接 http://www.jasongj.com/spark/cbo/
本文所述内容均基于 2018年9月17日 Spark 最新 Release 2.3.1 版本。后续将持续更新

Spark CBO 背景

上文Spark SQL 内部原理中介绍的 Optimizer 属于 RBO,实现简单有效。它属于 LogicalPlan 的优化,所有优化均基于 LogicalPlan 本身的特点,未考虑数据本身的特点,也未考虑算子本身的代价。

本文将介绍 CBO,它充分考虑了数据本身的特点(如大小、分布)以及操作算子的特点(中间结果集的分布及大小)及代价,从而更好的选择执行代价最小的物理执行计划,即 SparkPlan。

Spark CBO 原理

CBO 原理是计算所有可能的物理计划的代价,并挑选出代价最小的物理执行计划。其核心在于评估一个给定的物理执行计划的代价。

物理执行计划是一个树状结构,其代价等于每个执行节点的代价总合,如下图所示。

CBO 总代价

而每个执行节点的代价,分为两个部分

  • 该执行节点对数据集的影响,或者说该节点输出数据集的大小与分布
  • 该执行节点操作算子的代价

每个操作算子的代价相对固定,可用规则来描述。而执行节点输出数据集的大小与分布,分为两个部分:1) 初始数据集,也即原始表,其数据集的大小与分布可直接通过统计得到;2)中间节点输出数据集的大小与分布可由其输入数据集的信息与操作本身的特点推算。

所以,最终主要需要解决两个问题

  • 如何获取原始数据集的统计信息
  • 如何根据输入数据集估算特定算子的输出数据集

Statistics 收集

通过如下 SQL 语句,可计算出整个表的记录总数以及总大小

ANALYZE TABLE table_name COMPUTE STATISTICS;

从如下示例中,Statistics 一行可见, customer 表数据总大小为 37026233 字节,即 35.3MB,总记录数为 28万,与事实相符。

spark-sql> ANALYZE TABLE customer COMPUTE STATISTICS;
Time taken: 12.888 seconds

spark-sql> desc extended customer;
c_customer_sk  bigint   NULL
c_customer_id  string   NULL
c_current_cdemo_sk      bigint  NULL
c_current_hdemo_sk      bigint  NULL
c_current_addr_sk       bigint  NULL
c_first_shipto_date_sk  bigint  NULL
c_first_sales_date_sk   bigint  NULL
c_salutation   string   NULL
c_first_name   string   NULL
c_last_name    string   NULL
c_preferred_cust_flag   string  NULL
c_birth_day    int      NULL
c_birth_month  int      NULL
c_birth_year   
<think>好的,我现在需要回答用户关于Hive查询优化技巧和策略的问题。首先,我应该回顾用户提供的引用内容,确保回答准确并引用相关部分。用户提到了索引、分区、分桶、避免数据倾斜等,这些都是关键点。 首先,Hive的索引虽然在某些情况下有用,但引用[1]和[2]都提到索引可能不如分区和分桶有效。所以,我需要强调分区和分桶策略的重要性,并解释它们如何减少数据扫描量。 接下来,用户提到的数据倾斜问题,引用[1]、[4]、[5]都涉及这一点。需要详细说明如何通过调整Join策略,比如使用Map Join或Bucket Map Join来处理。特别是对于大表和小表的情况,Map Join有效,而Bucket Map Join适用于大表之间。 然后,优化执行引擎和参数配置也很重要。比如启用Tez或Spark引擎,调整并行执行等,引用[3]提到了并发执行,需要展开说明。 数据倾斜的解决方案包括使用skew join和distribute by,引用[4]提到了数据倾斜的来源,这里需要给出具体方法。另外,压缩和存储格式优化,比如ORC和Parquet,可以结合引用[2]提到的数据类型选择。 最后,用户可能需要知道如何应用这些技巧,比如在编写查询时使用谓词下推、列裁剪,或者利用窗口函数优化聚合操作,引用[3]中的窗口函数和group by技巧需要提及。 还要注意回答的结构,确保每个技巧清晰,并自然地添加引用。同时,生成的相关问题要覆盖用户可能进一步询问的方向,比如索引的适用场景、分区分桶的区别、处理数据倾斜的具体步骤等。</think>### Hive 查询优化技巧策略 #### 1. **合理使用分区分桶** - **分区(Partitioning)**:将表按特定字段(如日期、地区)划分为独立目录,查询时跳过无关分区。例如: ```sql CREATE TABLE sales (id INT, product STRING) PARTITIONED BY (dt STRING); ``` 查询时通过分区字段过滤可减少扫描数据量[^1]。 - **分桶(Bucketing)**:将数据按哈希值分布到固定数量的桶中,适合优化 JOIN 操作。例如: ```sql CREATE TABLE orders (order_id INT, user_id INT) CLUSTERED BY (user_id) INTO 32 BUCKETS; ``` 分桶后可启用 `Bucket Map Join` 提升大表关联效率[^5]。 --- #### 2. **优化 JOIN 操作** - **Map Join**:将小表加载到内存中广播到大表,避免 Shuffle。需满足小表条件(通常小于25MB): ```sql SET hive.auto.convert.join=true; ``` - **Bucket Map Join**:若两个表均分桶且桶数成倍数,可直接按桶关联,避免全表扫描。 - **处理数据倾斜**:对倾斜键添加随机前缀打散分布,或使用 `skew join` 自动优化: ```sql SET hive.optimize.skewjoin=true; ``` --- #### 3. **调整执行引擎参数** - **启用 Tez/Spark 引擎**:替换传统 MapReduce,提升 DAG 执行效率: ```sql SET hive.execution.engine=tez; ``` - **并发执行**:对无依赖的 Stage 开启并行: ```sql SET hive.exec.parallel=true; ``` - **合并小文件**:减少 Map 任务数量: ```sql SET hive.merge.mapfiles=true; ``` --- #### 4. **数据压缩存储优化** - **列式存储**:使用 ORC 或 Parquet 格式,支持谓词下推和列裁剪。例如: ```sql CREATE TABLE logs (ip STRING, url STRING) STORED AS ORC; ``` - **压缩算法**:启用 Snappy 或 Zlib 压缩,减少 I/O 开销: ```sql SET hive.exec.compress.output=true; ``` --- #### 5. **SQL 编写技巧** - **避免 `SELECT *`**:仅选择必要字段,减少数据传输量。 - **提前过滤数据**:在子查询或 CTE 中先过滤再关联。 - **利用窗口函数**:替代多重子查询,例如用 `ROW_NUMBER()` 去重[^3]。 --- #### 6. **统计信息 CBO** - 收集表统计信息以启用基于代价优化CBO): ```sql ANALYZE TABLE sales COMPUTE STATISTICS; ``` CBO 会自动选择最优执行计划[^2]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值