北川广海の梦

北川广海の梦

SQL的执行过程简析

101
2023-03-17

对于这个执行过程,过去总是模糊不清,含糊的知道,会有SQL语法解析,执行计划分析等等。近日在工作中有相关学习,故写此文记录。

SQL

SELECT id,name,age FROM Users WHERE id = 18;

先来看这样一句SQL,在人的角度可以从中获取几个关键信息:这是一个“SELECT”操作,从特定的某张表里,需要取出一系列字段,并且附加有查询条件。

而数据库无法直接从字符串中解析出这些信息,它需要做一些额外的处理。

词法分析 lexer

首先需要先解析SQL中的关键字,例如 SELECT、FROM、WHERE、以及SQL中涉及的字段、表达式等。

而具体的关键字,是需要被定义的,例如可以采用YYAC的格式进行定义:

%token <ident>
    invalid
    explainKwd "EXPLAIN"
    selectKwd "SELECT"
    insertKwd "INSERT"
    updateKwd "UPDATE"
    deleteKwd "DELETE"
    into "INTO"
    values "VALUES"
    set "SET"

词法分析就能从SQL语句中,将关键字提取成为token,这些token,就是后面语法分析需要用到的了。

语法分析 parser

拿到了token,相当于士兵收到了具体的命令,但是士兵并不知道这些命令能否进执行,它需要判断命令是否符合格式标准。

比如 select后面要么是字段、要么是*,不能紧接着就是WHERE。

这些格式定义同样需要声明:

SelectStmtBasic:
    "SELECT" SelectOpt FieldList
    {
        fields := &ast.FieldList{Fields: $3.(*ast.SelectFieldList).List}
        st := &ast.SelectStmt{
            SelectOpt: $2.(*ast.SelectOpt),
            Distinct: $2.(*ast.SelectOpt).Distinct,
            Fields: fields,
            }
        $$ = st
    }

以select为例,SELECT关键字后面,需要是 SelectOPt(例如 DISTINCT),和字段列表。如果语法解析器无法从定义中找到匹配的,那么说明这段SQL是无效的。

而如果能够匹配到对应的规则,那么语法解析器就会生成对应的抽象语法数节点。例如 select节点,它拥有一个fileds子节点,记录了要查询哪些字段,还有Distinct节点记录了是否需要进行去重。

而当整个SQL都被解析为AST(抽象语法树)之后,语法解析器的工作也就完成了。

执行优化器

对于一行SQL,只看它的ast,我们只能解析出,它要做什么;但是通往罗马的道路不止一条,数据库需要选择出最优的执行方案。
例如:

SELECT name,age FROM Users WHERE name = "Kaniu"

在name建立了索引的情况下,那么就可以通过访问name字段的索引,去进行检索,然后拿着主键id,再回表去拿到age。

这是其中一种方案,但它并不一定是最好的,有人可能会疑惑,索引不是能优化性能吗,这肯定就是最好的执行方案了呀。

在通常情况下,可能索引性能更好,但是如果这张Users只有1条数据,
如果还走索引的话,不仅需要走一遍name的索引,还需要回表到主键索引。而如果采用全表扫描的话,那么一次访问就可以了。

所以 这就是执行优化器存在的意义。它会根据数据库的统计信息,分析出开销最小的执行方案。

执行引擎 与 存储引擎

有了具体的执行方案,那么就需要开始干活了。执行引擎就是负责实现具体操作的。例如SORT操作,执行引擎就会采用对应的算法将数据排序。而存储引擎,则是提供原始数据的。例如要执行按索引过滤,那么执行引擎就需要从存储引擎中,将索引加载到内存。

在这一步执行完毕后,数据也被按照SQL中的要求进行过滤、排序、分组了。就可以将数据返回给客户端了。