当前位置:Gxlcms > 数据库问题 > 淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

时间:2021-07-01 10:21:17 帮助过:11人阅读

id,name,sex from student where sex=‘M‘ order by id;

运行这条SQL会用到多个操作符,如选择、投影、排序等。一种方法是以一定的顺序每次运行一个操作,每次计算的结果被实体化到一个暂时关系中以备后用。实体化计算的代价包含全部运算的代价和把中间结果写回磁盘的代价。当中磁盘I/O的代价非常高。


还有一种方法是在流水线上同一时候运行多个运算,一个运算结果传递给下一个。而不必保存到暂时关系中。在实现中,每一个运算符有3个迭代函数:open,close,get_next


openclose分别为打开一个运算符,关闭一个运算符。

get_next函数用于获取一行元组。

二、 OceanBase中的物理查询计划

2.1 物理操作符

在0.3版本号OceanBase中。物理上运算符接口为 ObPhyOperator。其定义例如以下:

/// 物理运算符接口
    class ObPhyOperator
    {
      public:
        /// 打开物理运算符。申请资源,打开子运算符等。构造row description
        virtual int open() = 0;

        /// 关闭物理运算符。释放资源。关闭子运算符等。

virtual int close() = 0; /// 获得下一行的引用 virtual int get_next_row(const common::ObRow *&row) = 0; };



ObPhyOperator定义了open,close,get_next_row 3个函数用于实现运算符的流水化操作。并依据子节点的个数定义了几种类型的运算符,它们都继承自ObPhyOperator.

  • ObNoChildrenPhyOperator:无子节点的运算符
  • ObSingleChildPhyOperator:仅仅有一个子节点的运算符
  • ObDoubleChildrenPhyOperator:有两个子节点的运算符
  • ObMultiChildrenPhyOperator:有多个子节点的运算符(0.4版本号才出现的)
    此外还有:
  • ObRowkeyPhyOperator:(不是非常清楚,自我认为是)带返回RowKey的运算符,也就是返回的时候不是返回Row。而是返回RowKey。

    磁盘表扫描运算符ObSstableScan继承自该类。

  • ObNoRowsPhyOperator:无返回列的运算符,如插入运算符ObInsert继承自该类

以上几个运算符依旧是接口部分,真正使用时的运算符如同在关系代数中所说的一般,但SQL语句并非全然的关系代数运算,为了方便实现时都会定义很多其它的运算符。
下面是0.3版本号时的部分运算符及继承关系摘录:

运算符类名 父类 作用
ObFilter ObSingleChildPhyOperator 选择运算符
ObProject ObSingleChildPhyOperator 投影运算符
ObGroupBy ObSingleChildPhyOperator 分组运算符
ObHashGroupBy ObGroupBy hash分组运算符
ObInsert ObSingleChildPhyOperator,ObNoRowsPhyOperator 插入运算符
ObJoin ObDoubleChildrenPhyOperator 连接运算符
ObLimit ObSingleChildPhyOperator 限制行数的运算符
ObMergeDistinct ObSingleChildPhyOperator 归并去重运算符
ObSort ObSingleChildPhyOperator 排序运算符
ObRpcScan ObPhyOperator MS全表扫描
ObSstableScan ObRowkeyPhyOperator 用于CS从磁盘或缓冲区扫描一个tablet
ObTableScan ObSingleChildPhyOperator 全表扫描符

实际上还有非常多运算符,这里没有一一列举,并且在后来的版本号里还会有很多其它的运算符会被加入进来。


这些运算符是物理查询计划的主要构成。

2.2 物理查询计划的定义

物理查询计划由一系列运算符构成。OceanBase中物理查询计划ObPhysicalPlan定义例如以下:

class ObPhysicalPlan
    {
        /*省略其它方法*/
      private:
        oceanbase::common::ObArray<ObPhyOperator *> phy_querys_;
        oceanbase::common::ObArray<ObPhyOperator *> operators_store_;
    };


与逻辑计划类似,operators_store_用于存储查询计划中使用到的全部运算符。在逻辑计划中我们已经知道,一个查询计划会有多个查询实例。在物理查询计划ObPhysicalPlan中与之相应的是phy_querys_ 保存每一个查询实例的第一个运算符。

三、 从逻辑计划怎样生成物理查询计划

转换步骤非常easy,加入逻辑计划。生存物理查询计划,演示样例代码例如以下:
trans.add_logical_plans(multi_plan);
physical_plan = trans.get_physical_plan(0);

trans是转换类ObTransformer类,该类的功能就是将逻辑计划转换为物理查询计划。

3.1 SQL的语法运行顺序

SQL作为一种声明式语言。它并不关心怎样取数这个过程,而是通过SQL语句它声明它所须要的数据,有系统为其挑出符合要求的数据。
之前在讨论逻辑计划时。没有讨论到这一点。可是SQL的语法运行顺序直接影响了计划的生成过程。
SQL的语法顺序和运行顺序并不一致。

以以下这条SQL为例:

select student.name,math.score, from student,math where student.sex=‘M‘ order by student.id;

其语法声明顺序为:

  • SELECT
  • FROM
  • WHERE
  • ORDER BY

但其运行顺序为:

  • FROM
  • WHERE
  • SELECT
  • ORDER BY

物理查询计划,显然是以SQL运行顺序为准的

3.2 OceanBase中生成物理查询计划的系列函数

逻辑计划生成物理查询计划或物理操作符的操作由以下一系列函数完毕.

//物理查询计划生成函数
ObPhysicalPlan* ObTransformer::generate_physical_plan(ObLogicalPlan *logical_plan)

//select 语句-->物理查询计划
int64_t ObTransformer::gen_phy_mono_select
//order by -->排序运算符
ObPhyOperator* ObTransformer::gen_phy_order_by
//distinct-->去重运算符
ObPhyOperator* ObTransformer::gen_phy_distinct
//group by-->分组运算符
ObPhyOperator* ObTransformer::gen_phy_group_by
//聚集函数-->聚集运算符
ObPhyOperator* ObTransformer::gen_phy_scalar_aggregate
//表连接运算
int ObTransformer::gen_phy_joins
//from-->多表连接
int ObTransformer::gen_phy_tables
//表-->表扫描查询计划
ObPhyOperator* ObTransformer::gen_phy_table
//select语句-->物理查询计划,调用gen_phy_mono_select完毕
ObPhysicalPlan* ObTransformer::gen_physical_select
//delete语句-->物理查询计划
ObPhysicalPlan* ObTransformer::gen_physical_delete
//insert语句-->物理查询计划
ObPhysicalPlan* ObTransformer::gen_physical_insert
//update语句-->物理查询计划
ObPhysicalPlan* ObTransformer::gen_physical_update


0.3中仅支持SELECT语句,其它语句还不支持。

其生成逻辑在gen_phy_mono_select中。与SQL的运行顺序一致.

int64_t ObTransformer::gen_phy_mono_select(
    ObLogicalPlan *logical_plan,
    ObPhysicalPlan *physical_plan,
    uint64_t query_id)
{
  //int err = OB_SUCCESS;
  int64_t idx = OB_INVALID_INDEX;
  ObSelectStmt *select_stmt = NULL;
  if (query_id == OB_INVALID_ID)
    select_stmt = dynamic_cast<ObSelectStmt*>(logical_plan->get_main_stmt());
  else
    select_stmt = dynamic_cast<ObSelectStmt*>(logical_plan->get_query(query_id));
  if (!select_stmt)
    return OB_INVALID_INDEX;

  ObSelectStmt::SetOperator set_type = select_stmt->get_set_op();
  if (set_type != ObSelectStmt::NONE)
  {
    //带set 的SELECT语句的物理查询计划生成
  }
  else
  {
    /* 普通Select语句*/

    ObPhyOperator *result_op = NULL;

    // 1. generate physical plan for base-table/outer-join-table/temporary table
    ObList<ObPhyOperator*> phy_table_list;
    ObList<ObBitSet> bitset_list;
    ObList<ObSqlRawExpr*> remainder_cnd_list;
    gen_phy_tables(
        logical_plan,
        select_stmt,
        physical_plan,
        phy_table_list,
        bitset_list,
        remainder_cnd_list);

    // 2. Join all tables
    if (phy_table_list.size() > 1)
      gen_phy_joins(
          logical_plan,
          select_stmt,
          physical_plan,
          phy_table_list,
          bitset_list,
          remainder_cnd_list);
    phy_table_list.pop_front(result_op);

    // 3. add filter(s) to the join-op/table-scan-op result
    if (remainder_cnd_list.size() >= 1)
    {
      ObFilter *filter_op = NULL;
      CREATE_PHY_OPERRATOR(filter_op, ObFilter, physical_plan);
      filter_op->set_child(0, *result_op);
      oceanbase::common::ObList<ObSqlRawExpr*>::iterator cnd_it;
      for (cnd_it = remainder_cnd_list.begin(); cnd_it != remainder_cnd_list.end(); cnd_it++)
      {
        ObSqlExpression filter;
        (*cnd_it)->fill_sql_expression(filter, this, logical_plan, physical_plan);
        filter_op->add_filter(filter);
      }
      result_op = filter_op;
    }

    // 4. generate physical plan for group by/aggregate
    if (select_stmt->get_group_expr_size() > 0)
      result_op = gen_phy_group_by(logical_plan, select_stmt, physical_plan, result_op);
    else if (select_stmt->get_agg_fun_size() > 0)
      result_op = gen_phy_scalar_aggregate(logical_plan, select_stmt, physical_plan, result_op);

    // 5. generate physical plan for having
    if (select_stmt->get_having_expr_size() > 0)
    {
      ObFilter *having_op = NULL;
      CREATE_PHY_OPERRATOR(having_op, ObFilter, physical_plan);
      ObSqlRawExpr *having_expr;
      int32_t num = select_stmt->get_having_expr_size();
      for (int32_t i = 0; i < num; i++)
      {
        having_expr = logical_plan->get_expr(select_stmt->get_having_expr_id(i));
        ObSqlExpression having_filter;
        having_expr->fill_sql_expression(having_filter, this, logical_plan, physical_plan);
        having_op->add_filter(having_filter);
      }
      having_op->set_child(0, *result_op);
      result_op = having_op;
    }

    // 6. generate physical plan for distinct
    if (select_stmt->is_distinct())
      result_op = gen_phy_distinct(logical_plan, select_stmt, physical_plan, result_op);

    // 7. generate physical plan for order by
    if (select_stmt->get_order_item_size() > 0)
      result_op = gen_phy_order_by(logical_plan, select_stmt, physical_plan, result_op);

    // 8. generate physical plan for limit
    if (select_stmt->get_limit() != -1 || select_stmt->get_offset() != 0)
    {
      ObLimit *limit_op = NULL;
      CREATE_PHY_OPERRATOR(limit_op, ObLimit, physical_plan);
      limit_op->set_limit(select_stmt->get_limit(), select_stmt->get_offset());
      limit_op->set_child(0, *result_op);
      result_op = limit_op;
    }

    // 8. generate physical plan for select clause
    if (select_stmt->get_select_item_size() > 0)
    {
      ObProject *project_op = NULL;
      CREATE_PHY_OPERRATOR(project_op, ObProject, physical_plan);
      project_op->set_child(0, *result_op);

      ObSqlRawExpr *select_expr;
      int32_t num = select_stmt->get_select_item_size();
      for (int32_t i = 0; i < num; i++)
      {
        const SelectItem& select_item = select_stmt->get_select_item(i);
        select_expr = logical_plan->get_expr(select_item.expr_id_);
        if (select_item.is_real_alias_)
        {
          ObBinaryRefRawExpr col_raw(OB_INVALID_ID, select_expr->get_column_id(), T_REF_COLUMN);
          ObSqlRawExpr col_sql_raw(*select_expr);
          col_sql_raw.set_expr(&col_raw);
          ObSqlExpression  col_expr;
          col_sql_raw.fill_sql_expression(col_expr);
          project_op ->add_output_column(col_expr);
        }
        else
        {
          ObSqlExpression  col_expr;
          select_expr->fill_sql_expression(col_expr, this, logical_plan, physical_plan);
          project_op ->add_output_column(col_expr);
        }
      }
      result_op = project_op;
    }

    physical_plan->add_phy_query(result_op, idx);
  }

  return idx;
}


四、 总结

物理查询计划的生成过程比逻辑计划和语法树解析部分更复杂。

你须要了解相关的基础知识包含关系代数查询,流水线方式下的运算符构成,SQL语法的运行顺序等。


欢迎光临我的站点----蝴蝶忽然的博客园----人既无名的专栏。
假设阅读本文过程中有不论什么问题。请联系作者,转载请注明出处!

淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

标签:play   delete   app   生成   code   操作符   selection   reg   art   

人气教程排行