大数据SQL调优专题——引擎优化

引入

通过前面对目前大数据领域常见数据处理技术执行原理的梳理,我们会发现一条SQL语句,想要最终转换成集群可执行的物理计划,通常会经过如下图所示的一系列过程:

通常这些数据处理技术都会抽象一个执行器*(Driver)* ,来负责这一系列的转换,校验和优化的工作,而执行器中,又会抽象出优化器*(Optimizer)*来负责优化,也就是图中用红色箭头标识出来的部分。

优化器的主要作用就是将用户各异的查询SQL在不影响期望查询结果的前提下,尽可能用开销最小的方式实现。所以一个引擎的性能,在很大程度上也是由其设计的优化器决定的。

数据库查询优化技术

数据库查询优化技术主要包括以下六项:

  1. 查询重用技术
  2. 查询重写规则
  3. 查询算法优化技术
  4. 并行查询优化技术
  5. 分布式查询优化技术
  6. 其他方面*(例如框架结构)*的优化技术

这六项技术构成了一个广义的数据库查询优化的概念,下面我们先了解一下这些技术:

查询重用技术

定义与原理:查询重用技术旨在通过缓存和复用之前查询的结果或中间数据来减少重复计算。当类似或相同的查询被多次执行时,优化器可以识别这些查询并利用缓存的数据来加速后续的查询处理。

应用与示例:例如,在关系型数据库中,查询结果缓存(Query Result Cache)可以存储之前执行的查询及其结果。当用户再次发出相同的查询时,数据库可以直接从缓存中返回结果,而无需重新执行整个查询计划。此外,物化视图(Materialized Views)也是查询重用的一种形式,它预先计算并存储了基于某些查询逻辑的数据,当用户需要类似的数据时,可以直接使用物化视图中的数据,大大提高了查询效率。

查询重写规则

定义与原理:查询重写规则是指根据特定的逻辑和语义,将原始查询转换为等价但更高效的查询形式。这通常涉及到对查询语句中的操作进行代数变换、逻辑优化或语义转换。

应用与示例

  • 代数重写规则:这包括投影合并(Projection Pushdown)和谓词下推(Predicate Pushdown)等。投影合并可以将多个投影操作合并为一个,减少不必要的数据扫描和处理。例如,将查询语句 SELECT a, b FROM (SELECT a, b, c FROM table) 重写为 SELECT a, b FROM table,直接从原始表中提取需要的数据。谓词下推可以将过滤条件尽可能早地应用于数据流中,减少后续操作的数据量。例如,将查询语句 SELECT * FROM (SELECT * FROM table WHERE a > 10) 重写为 SELECT * FROM table WHERE a > 10,直接在数据源处进行过滤。

  • 逻辑重写规则 :例如,视图合并*(View Merging)* 和连接消除*(Join Elimination)*等。视图合并可以将查询与视图的定义结合起来,避免不必要的嵌套查询。例如,用户查询一个基于多个表连接的视图时,优化器可以将视图的定义与用户的查询合并,生成一个更高效的查询计划。连接消除则是通过子查询的重写来消除不必要的连接操作,例如,当子查询的条件足够严格时,可以将子查询转换为半连接或反连接,从而减少数据扫描和连接操作的开销。

查询算法优化技术

定义与原理:查询算法优化技术关注于选择合适的数据处理算法来提升查询的性能。不同的查询操作(如排序、连接、聚合等)有不同的算法实现,优化器需要根据数据的特性、查询的需求和系统的资源来选择最高效的算法。

应用与示例

  • 连接算法选择 :例如,在大数据处理中,嵌套循环连接*(Nested Loop Join)* 适用于小表连接,而哈希连接*(Hash Join)* 和排序-合并连接*(Sort-Merge Join)*则适用于大规模数据的连接操作。哈希连接通过构建哈希表来加速连接操作,而排序-合并连接则需要对连接键进行排序,适用于已经排序的数据或需要利用索引的情况。

  • 排序算法选择 :对于大规模数据的排序,可以采用外部排序*(External Sort)* 算法,如归并排序*(Merge Sort)*,它可以有效地处理超出内存容量的数据。此外,还可以根据数据的分布特点选择不同的排序策略,如多路归并排序或并行排序,以提高排序的效率。

  • 按需调用算法 :例如,在分布式系统中,根据数据的分布和查询的复杂度,可以选择不同的算法来处理查询。对于分布式排序操作,可以采用分布式快速排序*(Distributed Quick Sort)* 或基于采样的排序算法*(Sampling-based Sort)*,以减少数据传输和排序的开销。

并行查询优化技术

定义与原理:并行查询优化技术利用多核处理器、集群或分布式系统的并行计算能力来加速查询处理。通过将查询任务分解为多个并行子任务,并在不同的计算节点或处理器核心上并行执行,可以显著提升查询的性能。

应用与示例

  • 体系结构支持的并行 :在大规模并行处理*(MPP)*架构中,查询可以被分解为多个独立的子查询,并在不同的计算节点上并行执行。例如,Greenplum数据库等MPP数据库系统通过将数据分布在多个Segment节点上,并在每个节点上并行执行查询操作,实现了高效的并行查询处理。

  • 负载平衡:在并行查询优化中,负载平衡是一个关键问题。优化器需要确保不同的并行子任务能够均匀地分布在各个计算节点上,避免出现某些节点过载而其他节点空闲的情况。这可以通过动态调整任务分配策略、数据分区策略或资源调度策略来实现。

  • 并行查询优化的其他方面 :例如,并行任务的粒度优化,通过调整任务的大小和划分方式来平衡并行效率和调度开销。此外,并行查询优化还需要考虑数据本地性*(Data Locality)*,尽量将数据和计算任务分配到同一节点上,减少数据传输的开销。

分布式查询优化技术

定义与原理:分布式查询优化技术旨在将查询的执行计划合理地分布在多个计算节点上,充分利用分布式系统的资源,同时减少数据传输和通信开销。这涉及到数据分布策略、任务调度策略、通信协议等方面的优化。

应用与示例

  • 数据分布优化 :例如,在分布式数据库中,数据可以根据某种策略*(如哈希分布、范围分布或列表分布)* 分布在不同的节点上。通过合理地分布数据,可以使查询能够充分利用分布式的资源,并减少数据传输的开销。例如,在Hadoop的HDFS分布式文件系统中,数据被分成多个分块*(Block)*,并以副本的形式存储在多个节点上,查询时可以根据分块的位置信息来选择最合适的节点进行数据读取和处理。

  • 分布式执行计划生成 :优化器需要生成适合分布式环境的执行计划,包括数据流的传输路径、任务的调度顺序和通信方式。例如,在Apache Spark的分布式计算框架中,查询的执行计划被转换为一系列的分布式计算任务*(如转换操作和动作操作)*,并通过DAG调度器进行调度和执行。

  • 通信优化 :例如,通过数据压缩、批量传输和智能路由等技术来减少分布式查询中的通信开销。此外,还可以采用点对点通信*(Peer-to-Peer Communication)* 或消息队列*(Message Queue)*来优化分布式系统中的数据传输和任务协作。

其他方面优化技术

定义与原理:除了上述的优化技术外,还有许多其他的优化技术可以从框架结构层面提升查询的性能。例如,内存优化、容错机制、元数据管理等方面都可以对查询性能产生重要影响。

应用与示例

  • 内存优化 :例如,在内存数据库或高性能计算框架中,通过高效的内存管理机制和数据结构来提高查询的访问速度。这包括内存数据缓存*(如Redis的内存缓存)* 、列式存储*(Columnar Storage)* 和零拷贝技术*(Zero-Copy)*等。列式存储可以减少数据扫描的范围,提高聚合和过滤操作的效率,而零拷贝技术可以减少数据在内存和磁盘之间的拷贝次数,从而提高数据传输的效率。

  • 容错机制 :在分布式系统中,容错机制对于保证查询的可靠性和稳定性至关重要。例如,通过检查点*(Checkpoint)* 和日志技术*(Logging)*来实现数据的持久化和故障恢复。当系统出现故障时,可以通过恢复检查点和重放日志来重新构建查询的执行状态,从而保证查询的最终一致性。

  • 元数据管理优化 :高效的元数据管理可以加速查询的解析和执行计划的生成。例如,通过维护详细的统计信息*(如表的大小、列的分布、索引的效率等)*来帮助优化器做出更好的决策。此外,元数据的更新和维护也需要进行优化,以减少对查询性能的负面影响。

大数据处理引擎优化

从优化的内容角度看,查询优化又分为代数优化和非代数优化,或称为逻辑优化和物理优化。逻辑优化主要依据关系代数的等价变换做一些逻辑变换。物理优化主要使用数据读取、表连接方式、表连接顺序、排序等技术对查询进行优化。例如查询重写规则属于逻辑优化,运用了关系代数和启发式规则。而查询算法优化则属于物理优化,运用了基于代价估算的多表连接算法求解最小花费的技术。

经过抽象,优化方式可以分为以下两种。

  1. 基于规则优化*(Rule-Based Optimization,RBO)*,即按照硬编码的一系列规则来决定SQL的执行计划。
  2. 基于代价优化*(Cost-Based Optimization,CBO)*,即根据优化规则对关系表达式进行转换,生成多个执行计划,再根据统计信息和代价模型计算各种可能"执行计划"的"代价",从中选择"代价"最低的执行方案。

在关系型数据库中,以Oracle、MySQL、PostgreSQL为首的查询引擎提供了大量的优化思路和经验,其主要优化手段有尽可能利用索引、减少或减小中间结果集*(即减少行数、减小列数)​*,以及消除子查询和选择JOIN的不同实现方式等。

而大数据引擎无法直接利用传统的一些优化方式进行优化,其大多采用减少或减小中间集,或者减少I/O和CPU等资源开销的方式进行。总体来说,大数据SQL优化还是沿用传统关系型数据库成熟的优化理论,再结合分布式处理特性做一些额外的处理来实现的。

因为各引擎之间底层实现方式*(即物理执行)*的不同,也存在一些差异。

RBO和CBO两种优化方式作为数据处理引擎优化的核心,因为各引擎之间底层实现方式*(即物理执行)*的不同,会存在一些差异,所以需要掌握其本质,才能更好的实现SQL优化能力的横向拓展。

RBO (Rule-Based Optimizer)

下面先介绍一下RBO,它是一种静态优化方法,其规则通常是基于经验、常见场景和 SQL 的语义预先设定好的。

其本质就是基于规则优化,核心就是根据查询中特定的语法结构、逻辑模式等,把一个SQL改写成另外一个更容易优化或执行效率更高的SQL。

这些规则会从多个方面对 SQL 查询进行优化,包括但不限于减少数据传输、简化表达式、优化条件判断、合并操作、利用索引等。不同的大数据处理引擎会根据自身的架构特点和实现细节,选择适用的 RBO 规则,并对其进行优化和扩展。

下面我们看看RBO 的常见优化规则:

1. 谓词下推 (Predicate Pushdown)

规则介绍: 将查询中的过滤条件*(WHERE 子句中的谓词)* 尽早地应用到数据源*(如表或分区)*上,以减少后续操作的数据量。

**优化原理:**通过尽早过滤数据,减少需要处理的数据量,从而加速后续的查询操作。

注意:

谓词下推需要满足一定的规则才会触发。

以表A INNER JOIN表B为例,假设表A为探测表,表B负责构建哈希表,那么结论如下。

  • ON语句只有在构建哈希表中可以下推。
  • WHERE AND语句在两表中都可以下推。
  • WHERE OR语句,对于ON关联键的过滤可以下推。
  • FULL JOIN在任何情况均不下推。

除了连接操作外,其他的操作符(例如UNION、投影、LIMIT、窗口函数)在满足规则的前提下可以下推。

2. 常量堆叠 (Constant Folding)

规则介绍: 将表达式中的常量计算部分提前计算出来,替换为常量值。(常量是指值不会改变的变量或表达式)

**优化原理:**常量堆叠的目的是在编译或解释阶段,尽可能地提前计算这些常量表达式,以减少运行时的计算量和存储开销。

3. 常量传递 (Constant Propagation)

**规则介绍:**表中a字段与常量进行等值查询,与此同时还通过AND连接了另外一个表达式,如果该表达式中含有a字段,那么查询优化器就可以将该表达式中的a字段直接替换为等值查询中的常量值,这个替换过程就是常量传递。也就是将常量值传递到其他表达式中,从而进一步简化表达式。

**优化原理:**通过传递常量值,减少表达式中的变量计算,性能得以优化。

4. 等式传递 (Equality Propagation)

**规则介绍:**将已知的等式关系应用于程序中的其他表达式,尝试替换或简化这些表达式。

**优化原理:**通过将等式关系传递到程序的各个部分,可以消除重复计算,减少不必要的资源开销,以此提高程序的执行效率。

5. 布尔表达式简化 (Boolean Expression Simplification)

**规则介绍:**目的是在保持表达式等效性的前提下,使用更简洁的形式来表示。

可以通过以下几种常见的技术来实现:

1)代数规则

也就是利用布尔代数的基本规则,如德·摩根定律、分配律、结合律、吸收律等,对表达式进行变换和简化。

例如

sql 复制代码
SELECT *
FROM user_info
WHERE NOT (user_id = 123 AND name ='chaos');

=>

SELECT
FROM user_info
WHERE NOT(user_id = 123)
   OR NOT(name ='chaos');
2)逻辑等价变换

也就是利用布尔逻辑中的等价关系,将一个布尔表达式转化为与之等价的另一个表达式。

例如:

sql 复制代码
SELECT *
FROM user_info
WHERE name='chaos'
AND (age = 18 OR user_id = 123);

=>

SELECT *
FROM user_info
WHERE (name = 'chaos' AND age = 18)
   OR (name = 'chaos' AND user_id = 123)
3)常量传播和折叠

对于包含常量的表达式,优化器可以直接计算结果并将表达式替换为常量值,从而简化整个表达式。

4)短路规则

根据短路逻辑,如果表达式中的某个子表达式已经确定结果,则可以提前终止计算并简化表达式。

5)提取公共项

对于具有共同子表达式的表达式,可以将这些共同项提取出来,以减少不必要的计算过程,从而提升任务的执行效率。

例如:

sql 复制代码
SELECT *
FROM user_info
WHERE (name = 'chaos' AND age = 18)
   OR (name = 'chaos' AND user_id = 123)

=>

SELECT *
FROM user_info
WHERE name='chaos'
AND (age = 18 OR user_id = 123);

6. BETWEEN-AND 重写

规则介绍: 将 BETWEEN 重写为两个比较运算符,如将 BETWEEN a AND b 重写为 >= a AND <= b。
**优化原理:**主要目的是,当字段上已建立索引时,可以通过索引扫描代替基于谓词的全表扫描,从而显著提高查询效率。

7. NOT 取反重写 (NOT Rewriting)

**规则介绍:**将表达式中的 NOT 取反操作重新表达,例如,NOT (x > 5) 重写为 x <= 5。

**优化原理:**NOT取反重写的目的是通过等价转换NOT表达式,使得查询优化器可以更好地利用索引或其他优化技术来提高查询性能。

8. 简化 IF/CASE-WHEN 条件表达式

**规则介绍:**与布尔表达式简化类似,当IF或CASE WHEN条件判断的结果在解释或编译阶段就可以确定时,优化器将会传递条件判断后的结果。

**优化原理:**通过减少已确定的条件判断,不需要在执行过程中逐个计算,从而提升查询语句的执行效率。

9. 优化 LIKE 正则表达式

**规则介绍:**在关系型数据库中,优化LIKE正则表达式的目的是尽可能地利用索引来加快查询速度。而在大数据引擎中,因为大多不支持索引、不支持事务,所以在优化时会尝试替换为更快的StartsWith或EndsWith底层方法,从而提升查询速度。

特殊正则表达式的具体简化规则

|--------------|------------|
| 正则表达式 | 实际执行时 |
| LIKE 'abc%' | StartsWith |
| LIKE '%abc' | EndsWith |
| LIKE '%abc%' | Contains |
| LIKE 'abc' | EqualTo |

10. 简化 CAST 表达式

**规则介绍:**移除或简化不必要的数据类型转换(CAST 表达式)。例如,如果表达式 CAST(column AS INT) 已经确保 column 是整数,则直接使用 column。

**优化原理:**减少类型转换的开销,提升查询性能。

11. 简化 UPPER/LOWER 表达式

**规则介绍:**移除或简化不必要的字符串大小写转换。

**优化原理:**减少字符串操作的开销,优化查询性能。

12. 优化二元表达式 (Binary Expression Optimization)

**规则介绍:**与常量堆叠类似,将二元表达式(如加法、乘法)进行优化,例如,利用数学等价性将 a + b - a 简化为 b。

**优化原理:**简化表达式,减少计算量,提升查询速度。

13. 简化复杂类型数据结构的操作符

**规则介绍:**与布尔表达式简化类似,将作用于复杂类型数据结构(如数组、结构体)的操作符简化,例如,将 array[0] 提取为单独的列。

**优化原理:**通过简化对复杂类型的操作,减少处理的复杂度,提升查询性能。

14. 合并投影 (Projection Merging)

**规则介绍:**合并投影就是将多个相邻的投影操作合并为一个更简洁的投影操作,去除中间过程中不必要的数据传输或计算过程,从而提升查询效率。例如,SELECT a FROM (SELECT a, b FROM table) 可以合并为 SELECT a FROM table。

**优化原理:**减少数据传输和处理的开销,提升查询效率。

15. 列裁剪 (Column Pruning)

**规则介绍:**列裁剪就是在查询过程中,只筛选那些在查询或投影操作中实际用到的字段,而忽略没有被引用的不必要的字段。例如,查询 SELECT a FROM table 会只读取列 a,而不会读取表中的其他列。

**优化原理:**减少数据传输和存储的开销,提高查询速度。

16. 优化冗余别名 (Redundant Alias Optimization)

**规则介绍:**移除或优化查询中的冗余别名。例如,如果 SELECT column AS c FROM table 中的别名 c 并未在后续查询中使用,可以省略。

**优化原理:**减少不必要的内存占用和处理开销,提升查询性能。

17. 替换 NULL 表达式 (NULL Expression Replacement)

**规则介绍:**替换NULL表达式就是将NULL表达式替换为字面量,阻止NULL表达式传播,从而避免在执行阶段产生不必要的资源开销,以此提升查询效率。

例如:

sql 复制代码
SELECT COUNT(NULL) AS cnt
FROM t;

=>

SELECT O AS cnt;

因为COUNT(NULL)没有意义,所以返回的结果是0。因此在优化阶段,优化器将直接传递计数后的结果到查询任务中,以避免在实际执行时产生不必要的资源开销。

18. CONCAT 合并 (CONCAT Merging)

**规则介绍:**与常量堆叠类似,当CONCAT表达式在解释或编译阶段就可以推算出结果时,优化器将会直接传递计算后的结果。

**优化原理:**减少字符串连接的次数,免去执行阶段中不必要的资源开销,提升查询效率。

19. 等式变换 (Equivalence Transformation)

**规则介绍:**与NOT取反重写类似,将等式表达式转变为更高效的等价形式。例如,a = b 可以与 b = a 互换位置,根据查询引擎的索引规则选择更优的方向。

**优化原理:**利用索引或其他优化机制,提升查询性能。

20. 不等式变换 (Inequality Transformation)

**规则介绍:**与等式传递类似,优化器会尝试去除查询语句中重复或不必要的表达式。

**优化原理:**通过调整不等式的顺序,优化查询条件与数据分布的匹配度,提升查询效率。

CBO(Cost-Based Optimization)

CBO是一种根据成本模型来选择最优执行计划的查询优化技术。与RBO不同,CBO通过收集和分析表的统计信息*(如表的行数、列的数据分布、索引的效率等)*来估算不同执行计划的成本,进而选择成本最低的执行计划。

基本概念

  • 成本模型:CBO的核心是成本模型,它用于评估执行计划的资源消耗。成本模型通常基于磁盘I/O、CPU利用率、网络传输等因素来估算执行计划的时间和资源需求。

  • 统计信息:CBO依赖于收集表的统计信息来生成准确的成本估算。这些统计信息包括表的行数、列的数据分布、索引的使用情况等。

  • 执行计划选择:CBO通过比较不同执行计划的成本来选择最优的执行计划。成本最低的执行计划被认为是效率最高的。

其核心主要是以下两点:

  1. 代价估算的维度和度量
    收集必要的统计信息是CBO工作的前提,CBO和统计信息之间的关系如同鱼水之交,代价估算的模型算法再好,如果没有准确、及时的统计信息,那么无异于巧妇难为无米之炊。统计信息需要做到基本信息能够自动化收集、自动化更新,高级统计信息可以手动收集,从而为CBO提供可靠的、多维度的统计信息。
  2. 代价估算的模型算法
    代价估算是优化器能否找到最优计划的关键因素,代价估算做不好,优化器不可能做好。代价估算涉及统计信息的推导和代价模型。统计信息的推导依赖于诸如原始表的统计信息、中间算子的推导算法、对数据的各种假设(均匀性假设、独立性假设、包括性假设、包含性假设)以及在一些极端情况下的猜测。因此统计信息的推导存在大量的不确定性,也正是因为这些不确定性,极大地加剧了优化器寻找最优解的难度。

工作原理

  • 收集统计信息 :在查询执行之前,CBO会收集表的统计信息。这些统计信息可以通过分析表或索引来收集。例如,可以使用ANALYZE TABLE命令来收集表的统计信息。

  • 生成执行计划:CBO根据统计信息生成多个可能的执行计划。每个执行计划都对应不同的操作顺序和算法。

  • 估算成本:CBO根据成本模型估算每个执行计划的成本。

  • 选择最优执行计划:CBO选择成本最低的执行计划作为最终的执行计划。

寻找最优策略这类动态规划算法,很容易陷入穷举所有可能性的范畴,从而带来灾难性的时间、空间复杂度问题。除此之外,还需要考虑SQL语法树的深度、SQL语法树的种类(例如左深树还是稠密树)​、具体物理实现的算法策略(例如JOIN可以是重分区连接,也可以是复制连接)​。可以说从学术到工程上,从度量到算法实现上,各类优化方案层出不穷,直到现在业界仍然没有一款堪称完美的CBO策略​。总而言之,CBO虽然仍需要不断打磨,在实际工作中不断探索改进优化的路径,但依然不影响其强大的功能和显著的调优效果。

总结

CBO适用于复杂查询,可以根据统计信息和成本模型生成高效的执行计划,适用于不同的数据分布和查询模式。但它需要准确的统计信息,且在某些情况下可能需要更多的计算资源。RBO在简单查询场景下表现出色,因为它基于预定义规则生成执行计划。然而,它无法应对复杂查询,因此在现代数据库系统中的应用受到限制。

在大数据引擎中,由于统计信息获取的时机较难抉择,例如在处理数据时进行信息统计,会拖慢任务运行的时间;在数据处理完毕后再定期调度统计,那么信息可能失真,从而导致策略算法因为缺少统计信息的支撑,不一定能够计算出最优解。因此,虽然Hive和Spark分别做了CBO的部分尝试*(Flink没有CBO策略)​*,但原则上还是以RBO的优化策略为主。

CBO和RBO的共存使得数据库系统能够更好地适应不同类型的查询,充分发挥各自的优势。了解这两种优化方式的特点以及适用场景,对于SQL性能优化至关重要,可以确保在各种情况下都能取得最佳结果。

相关推荐
知初~2 小时前
出行项目案例
hive·hadoop·redis·sql·mysql·spark·database
山猪打不过家猪3 小时前
ASP.NET Core Clean Architecture
java·数据库·asp.net
qwy7152292581633 小时前
13-R数据重塑
服务器·数据库·r语言
Bio Coder3 小时前
R语言安装生物信息数据库包
开发语言·数据库·r语言
钊兵4 小时前
数据库驱动免费下载(Oracle、Mysql、达梦、Postgresql)
数据库·mysql·postgresql·oracle·达梦·驱动
weixin_425878235 小时前
Redis复制性能优化利器:深入解析replica-lazy-flush参数
数据库·redis·性能优化
狮歌~资深攻城狮5 小时前
HBase性能优化秘籍:让数据处理飞起来
大数据·hbase
左灯右行的爱情5 小时前
Redis数据结构总结-listPack
数据结构·数据库·redis
Elastic 中国社区官方博客6 小时前
Elasticsearch Open Inference API 增加了对 Jina AI 嵌入和 Rerank 模型的支持
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·jina