鲲鹏社区首页
中文
注册
带你走进openGauss数据库核心技术(一)

带你走进openGauss数据库核心技术(一)

openGauss

发表于 2020/07/23

0

前言:

SQL引擎是数据库重要的子系统之一,它对上负责承接应用程序发送过来的SQL语句,对下负责指挥执行器运行执行计划。其中优化器作为SQL引擎中最重要、最复杂的模块,被称为数据库的“大脑”,优化器产生的执行计划的优劣直接决定数据库的性能。本文将对SQL引擎的各个模块进行全面的说明。

SQL引擎概览

SQL引擎是数据库系统重要组成部分,它的主要职责是负责将应用程序输入的SQL在当前负载场景下生成高效的执行计划,在SQL的高效执行上扮演重要角色。SQL在SQL引擎里的执行过程如图所示。

 SQL执行流程: 


从上图中可以看出,应用程序的SQL需要经过SQL解析生成逻辑执行计划、经过查询优化生成物理执行计划,然后将物理执行计划转交给查询执行引擎做物理算子的执行操作。

SQL解析

SQL语句在数据库管理系统中的编译过程符合编译器实现的常规过程,需要进行词法分析、语法分析和语义分析。

在SQL语言标准中,确定了SQL语言的关键字以及语法规则信息,SQL解析器在做词法分析的过程中会将一个SQL语句根据关键字信息以及间隔信息划分为独立的原子单位,每个单位以一个词的方式展现,例如如下SQL语句:

(1) 词法分析:从查询语句中识别出系统支持的关键字、标识符、操作符、终结符等,每个词确定自己固有的词性。

(2) 语法分析:根据SQL语言的标准定义语法规则,使用词法分析中产生的词去匹配语法规则,如果一个SQL语句能够匹配一个语法规则,则生成对应的抽象语法树(Abstract Syntax Tree,AST)。

(3)语义分析:对语法树(AST)进行有效性检查,检查语法树中对应的表、列、函数、表达式是否有对应的元数据,将抽象语法树转换为逻辑执行计划(关系代数表达式)。

SELECT w_name FROM warehouse WHERE w_no = 1;

可以划分的关键字、标识符、操作符、常量等原子单位,如下表所示。

词性

内容

关键词

SELECT、FROM、WHERE

标识符

w_name、warehouse、w_no

操作符

=

常量

1

语法分析会根据词法分析获得的词来匹配语法规则,最终生成一个抽象语法树(AST),每个词作为语法树的叶子结点出现,如下图所示。

抽象语法树: 


抽象语法树表达的语义还仅仅限制在能够保证应用的SQL语句符合SQL标准的规范,但是对于SQL语句的内在含义还需要做有效性的检查。

(1) 检查关系的使用:FROM子句中出现的关系必须是该查询对应模式中的关系或视图。

(2) 检查与解析属性的使用:在SELECT句中或者WHERE子句中出现的各个属性必须是FROM子句中某个关系或视图的属性。

(3) 检查数据类型:所有属性的数据类型必须是匹配的。

在有效性检查的同时,语义分析的过程还是有效性语义绑定(Bind)的过程,通过语义分析的检查,抽象语法树就转换成一个逻辑执行计划,逻辑执行计划可以通过关系代数表达式的形式来表现,如下图所示。

关系代数表达式: 


查询优化

依据优化方法的不同,优化器的优化技术可以分为:

(1)基于规则的查询优化(Rule Based Optimization,RBO):根据预定义的启发式规则对SQL语句进行优化。

(2)基于代价的查询优化(Cost Based Optimization,CBO):对SQL语句对应的待选执行路径进行代价估算,从待选路径中选择代价最低的执行路径作为最终的执行计划。

(3)基于机器学习的查询优化(AI Based Optimization,ABO):收集执行计划的特征信息,借助机器学习模型获得经验信息,进而对执行计划进行调优,获得最优的执行计划。

近年来AI技术,特别是在深度学习领域,发展迅速,基于机器学习的优化器在建模效率、估算准确率和自适应性等方面都有很大优势,有望打破RBO和CBO基于静态模型的限制,通过对历史经验的不断学习,将目标场景的模式进行抽象化,形成动态的模型,自适应地针对用户的实际场景进行优化。openGauss采用基于CBO的优化技术,另外在ABO方面也在进行积极探索。

1. 查询重写

查询重写就是把用户输入的SQL语句转换为更高效的等价SQL,查询重写遵循两个基本原则:

等价性:原语句和重写后的语句,输出结果相同。

高效性:重写后的语句,比原语句在执行时间和资源使用上更高效。

2. 常见的查询重写技术

openGauss几个关键的查询重写技术:常量表达式化简、子查询优化、选择下推和等价推理等。

(1)常量表达式化简

常量表达式即用户输入SQL语句中包含运算结果为常量的表达式,分为算数表达式、逻辑运算表达式、函数表达式,查询重写可以对常量表达式预先计算以提升效率。

示例1:该语句为典型的算数表达式查询重写,经过重写之后,避免了在执行时每条数据都需要进行1+1运算。

SELECT * FROM t1 WHERE c1 = 1+1;
SELECT * FROM t1 WHERE c1 = 2;

示例2:该语句为典型的逻辑运算表达式,经过重写之后,条件永远为false,可以直接返回0行结果,避免了整个语句的实际执行。

SELECT * FROM t1 WHERE  1=0  AND a=1;
SELECT * FROM t1 WHERE  false;

示例3:该语句包含函数表达式,由于函数的入参为常量,经过重写之后,直接把函数运算结果在优化阶段计算出来,避免了在执行过程中逐条数据的函数调用开销。

SELECT * FROM t1 WHERE c1 =  ADD(1,1);
SELECT * FROM t1 WHERE c1 =  2;

(2)子查询优化

由于子查询表示的结构更清晰,符合人的阅读理解习惯,用户输入的SQL语句往往包含了大量的子查询。子查询有几种分类方法,根据子查询是否可以独立求解,分为相关子查询和非相关子查询。

相关子查询:相关子查询是指子查询中有依赖父查询的条件,例如:

SELECT * FROM t1 WHERE EXISTS (SELECT t2.c1 FROM t2 WHERE t1.c1=t2.c1);

语句中子查询依赖父查询传入t1.c1的值。

非相关子查询:非相关子查询是指子查询不依赖父查询,可以独立求解,例如:

SELECT * FROM t1 WHERE c1 = 1+1;
SELECT * FROM t1 WHERE c1 = 2;

SELECT * FROM t1 WHERE EXISTS (SELECT t2.c1 FROM t2)

语句中子查询没有依赖父查询的条件。

其中,相关子查询需要父查询执行出一条结果,然后驱动子查询运算,这种嵌套循环的方式执行效率较低。如果能把子查询提升为父查询同级别,那么可以子查询中的表就能和父查询中的表直接做Join操作,由于Join操作可以有多种实现方法,优化器就可以从多种实现方法中选择最优的一种,就有可能提高查询的执行效率,另外优化器还能够应用Join Reorder优化规则对不同的表的连接顺序进行交换,进而有可能产生更好的执行计划。

示例:该语句为典型的子查询提升重写,重写之后利用Hash Join可以提升查询性能。

SELECT * FROM t1 WHERE t1.c1 IN (SELECT t2.c1 FROM t2);
SELECT * FROM t1 Semi Join t2 ON t1.c1 = t2.c1;

(3)选择的下推和等价推理

选择的下推能够极大的降低上层算子的计算量,从而达到优化的效果,如果选择条件有存在等值操作,那么还可以借助等值操作的特性来实现等价推理,从而获得新的选择条件。

例如,假设有两个表t1、t2分别包含[1,2,3,..100]共100行数据,那么查询语句

SELECT t1.c1, t2.c1 FROM t1 JOIN t2 ON t1.c1=t2.c1 WHERE t1.c1=1;

查询重写前后对比图:


(4)外连接消除

外连接和内连接的主要区别是对于不能产生连接结果的元组需要补NULL值,如果SQL语句中有过滤条件符合空值拒绝的条件(即会将补充的NULL值再过滤掉),则可以直接消除外连接。

示例:外连接转成内连接之后,便于优化器应用更多的优化规则,提高执行效率。

SELECT * FROM t1  FULL JOIN t2 ON t1.c1 = t2.c1 WHERE t1.c2 > 5 AND t2.c3 < 10;
SELECT * FROM t1  INNER JOIN  t2 ON t1.c1 = t2.c2 WHERE t1.c2 > 5 AND t2.c3 < 10;

(5)DISTINCT消除

DISTINCT列上如果有主键约束,则此列不可能为空,且无重复值,因此不需要DISTINCT操作,减少计算量。

示例:c1列上有的主键属性决定了无需做DISTINCT操作。语句如下:

CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT);
SELECT  DISTINCT(c1) FROM t1;
SELECT c1 FROM t1;

(6)IN谓词展开

示例:将IN操作符改写成等值的过滤条件,便于借助索引减少计算量。语句如下:

SELECT * FROM t1 WHERE  c1 IN (10,20,30);
SELECT * FROM t1 WHERE  c1=10 or c1=20 OR c1=30;

(7)视图展开

视图从逻辑上可以简化书写SQL的难度,提高查询的易用性,而视图本身是虚拟的,因此在查询重写的过程中,需要对视图展开。

示例:可以将视图查询重写成子查询的形式,然后再对子查询做简化。语句如下:

CREATE VIEW v1 AS (SELECT * FROM t1,t2 WHERE t1.c1=t2.c2);
SELECT * FROM v1;
SELECT * FROM (SELECT * FROM t1,t2 WHERE t1.c1=t2.c2) as v1;
SELECT * FROM t1,t2 WHERE t1.c1=t2.c2;

openGauss开源社区官方网站:

https://opengauss.org/

openGauss组织仓库:

https://gitee.com/opengauss

openGauss镜像仓库:

https://github.com/opengauss-mirror

本页内容