
# Spark CBO调优
#### 操作场景
SQL语句转化为具体执行计划是由SQL查询编译器决定的，同一个SQL语句可以转化成多种物理执行计划，如何指导编译器选择效率最高的执行计划，这就是优化器的主要作用。传统数据库（例如Oracle）的优化器有两种：基于规则的优化器(Rule-Based Optimization,RBO)和基于代价的优化器(Cost-Based Optimization,CBO)。
- RBO RBO使用的规则是根据经验形成的，只要按照这个规则去写SQL语句，无论数据表中的内容怎样、数据分布如何，都不会影响到执行计划。
  
- CBO CBO是根据实际数据分布和组织情况，评估每个计划的执行代价，从而选择代价最小的执行计划。
  
 
目前Spark的优化器都是基于RBO的，已经有数十条优化规则，例如谓词下推、常量折叠、投影裁剪等，这些规则是有效的，但是它对数据是不敏感的。导致的问题是数据表中数据分布发生变化时，RBO是不感知的，基于RBO生成的执行计划不能确保是最优的。而CBO的重要作用就是能够根据实际数据分布估算出SQL语句，生成一组可能被使用的执行计划中代价最小的执行计划，从而提升性能。
目前CBO主要的优化点是Join算法选择。举个简单例子，当两个表做Join操作，如果其中一张原本很大的表经过Filter操作之后结果集小于BroadCast的阈值，在没有CBO情况下是无法感知大表过滤后变小的情况，采用的是SortMergeJoin算法，涉及到大量Shuffle操作，很耗费性能；在有CBO的情况下是可以感知到结果集的变化，采用的是BroadcastHashJoin算法，会将过滤后的小表BroadCast到每个节点，转变为非Shuffle操作，从而大大提高性能。
#### 操作步骤
Spark CBO的设计思路是，基于表和列的统计信息，对各个操作算子（Operator）产生的中间结果集大小进行估算，最后根据估算的结果来选择最优的执行计划。
1. 设置配置项。
   - 在"spark-defaults.conf"配置文件中增加配置项"spark.sql.cbo"，将其设置为true，默认为false。
   
   - 在客户端执行SQL语句***set spark.sql.cbo=true***进行配置。
    
2. 执行统计信息生成命令，得到统计信息。
   ![](https://support.huaweicloud.com/devg-mrs/public_sys-resources/note_3.0-zh-cn.png)
   此步骤只需在运行所有SQL前执行一次。如果数据集发生了变化（插入、更新或删除），为保证CBO的优化效果，需要对有变化的表或者列再次执行统计信息生成命令重新生成统计信息，以得到最新的数据分布情况。
   - 表：执行***COMPUTE STATS FOR TABLE src***命令计算表的统计信息，统计信息包括记录条数、文件数和物理存储总大小。
   
   - 列：
     - 执行***COMPUTE STATS FOR TABLE src ON COLUMNS***命令计算所有列的统计信息。
     
     - 执行***COMPUTE STATS FOR TABLE src ON COLUMNS*** *name,age* 命令计算表中name和age两个字段的统计信息。
       当前列的统计信息支持四种类型：数值类型、日期类型、时间类型和字符串类型。对于数值类型、日期类型和时间类型，统计信息包括：Max、Min、不同值个数(Number of Distinct Value,NDV)、空值个数(Number of Null)和Histogram（支持等宽、等高直方图）；对于字符串类型，统计信息包括：Max、Min、Max Length、Average Length、不同值个数(Number of Distinct Value,NDV)、空值个数(Number of Null)和Histogram（支持等宽直方图）。
       
      
    
3. CBO调优
   - 自动优化：用户根据自己的业务场景，输入SQL语句查询，程序会自动去判断输入的SQL语句是否符合优化的场景，从而自动选择Join优化算法。
   
   - 手动优化：用户可以通过***DESC FORMATTED src***命令查看统计信息，根据统计信息的分布，人工优化SQL语句。
    
 
