暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss数据库源码解析系列文章——执行器解析(三)

Gauss松鼠会 2021-09-19
1097

Gauss松鼠会

学习 探索 分享数据库前沿知识和技术 共建数据库技术交流圈

关注
在上一篇openGauss数据库源码解析系列文章——执行器解析(二)中,介绍了执行器解析中“7.4 表达式计算”及“7.5 编译执行”的相关内容,本篇将介绍“7.6 向量化引擎”的精彩内容。完整版内容请查看CSDN·Gauss松鼠会专栏博客,以下内容为章节试读:

7.6  向量化引擎

传统的行执行引擎大多采用一次一元组的执行模式,这样在执行过程中CPU大部分时间并没有用来处理数据,更多的是在遍历执行树,就会导致CPU的有效利用率较低。而在面对OLAP场景巨量的函数调用次数,需要巨大的开销。为了解决这一问题,openGauss中增加了向量化引擎。向量化引擎使用了一次一批元组的执行模式,能够大大减少遍历执行节点的开销。一次一批元组的数据运载方式也为某些表达式计算的SIMD(single instruction, multiple data,单指令多数据)化提供了机会,SIMD化能够带来性能上的提升。同时向量化引擎还天然对接列存储,能够较为方便地在底层扫描节点装填向量化的列数据。

向量化引擎的执行算子类似于行执行引擎,包含控制算子、扫描算子、物化算子和连接算子。同样会使用节点表示,继承于行执行节点,执行流程采用递归方式。主要包含的节点有:CStoreScan(顺序扫描),CStoreIndexScan(索引扫描),CStoreIndexHeapScan(利用Bitmap获取元组),VecMaterial(物化),VecSort(排序),VecHashJoin(向量化哈希连接)等,下面将逐一介绍这些执行算子。

7.6.1  控制算子

1. VecResult算子

VecResult算子用于处理只有一个结果返回或WHERE过滤条件为常量的情况,对应的代码源文件是“vecresult.cpp”;对应的主要数据结构是VecResult,VecResult继承于BaseResult。VecResult算子相关的函数包括ExecInitVecResult(初始化节点)、ExecVecResult(执行节点)、ExecReScanVecResult(重置节点)、ExecEndVecResult(退出节点)。

ExecInitVecResult函数用于初始化VecResult执行算子。执行流程如图一所示,主要执行流程如下。

(1) 创建并初始化VecResult执行节点,并为节点创建表达式上下文。
(2) 调用“ExecInitResultTupleSlot(estate, &res_state->ps)”函数分配存储投影结果的slot。
(3) 调用投影表达式初始化函数ExecInitVecExpr依次对ps.targetlist、ps.qual和resconstantqual进行初始化。
(4) 分别调用ExecAssignResultTypeFromTL函数和ExecAssignVectorForExprEval函数进行扫描描述符的初始化和投影结构的创建。

图一  ExecInitVecResult函数执行流程

ExecVecResult函数是执行VecResult的主体函数。执行流程如图二所示,主要执行流程如下。

(1) 检查是否需要计算常量表达式。
(2) 若需要则重新计算表达式,设置检查标识(如果常量计算表达式结果为false时,则设置约束检查标识位)。
(3) 获取结果元组。

图二 ExecVecResult函数执行流程

ExecReScanVecResult函数用于重新执行扫描计划。

ExecEndVecResult函数用于在执行结束时释放执行过程中申请的相关资源(包括存储空间等)。

2. VectorModifyTable算子

VecModifyTable算子用于处理INSERT、UPDATE、DELETE操作,对应的代码源文件是“vecmodifytable.cpp”;对应的主要数据结构是VecModifyTableState,VecModifyTableState继承于ModifyTableState。具体定义代码如下所示:

    typedef struct VecModifyTableState : public ModifyTableState {
    VectorBatch* m_pScanBatch; /* 工作元组 */
    VectorBatch* m_pCurrentBatch; /* 输出元组 */
    } VecModifyTableState;

    VecModifyTable算子相关的函数包括ExecInitVecModifyTable(初始化节点)、ExecVecModifyTable(执行节点)、ExecEndVecModifyTable(退出节点)。

    ExecInitVecModifyTable函数用于初始化VecModifyTable算子,调用ExecInitModifyTable函数实现算子的初始化。

    ExecVecModifyTable函数是执行VecModifyTable算子的主体函数,循环地从子计划中获取目标列并根据要求修改每一列,通过“switch(operation)”处理不同的修改操作,具体的修改操作包括CMD_INSERT(插入)、CMD_DELETE(删除)、CMD_UPDATE(更新)。

    ExecEndVecModifyTable函数用于在执行VecModifyTable算子结束时调用ExecEndModifyTable函数清除相关资源。

    3. VecAppend算子

    VecAppend算子用于处理包含一个或多个子计划的链表,通过遍历子计划链表逐个执行子计划,对应的代码源文件是“vecappend.cpp”;对应的主要数据结构是VecAppendState,VecAppendState继承于AppendState。

    VecAppend算子相关的函数包括ExecInitVecAppend(初始化节点)、ExecVecAppend(执行节点)、ExecReScanAppend(重置节点)、ExecEndVecAppend(退出节点)。

    ExecInitVecAppend函数用于初始化VecAppend算子。执行执行流程如图三所示,主要执行流程如下。

    (1) 创建并初始化执行节点VecAppend。
    (2) 分配存储投影结果的slot。
    (3) 循环初始化子计划链表。
    (4) 初始化扫描描述符并设置初始迭代。

    图三  ExecInitVecAppend函数执行流程

    ExecVecAppend函数是执行VecAppend算子的主体函数。执行流程如图四所示,每次从子计划中获取一条元组,当取回全部元组时,移动到下一个子计划,直到执行全部子计划。

    图四  ExecVecAppend执行流程

    ExecEndVecAppend函数用于在执行结束时清理VecAppend算子,释放相应的子计划。

    7.6.2  扫描算子

    1. CStoreScan算子

    CStoreScan算子用于扫描基础表,按顺序扫描基础表,对应的代码源文件是“veccstore.cpp”;CStoreScan算子对应的主要数据结构是CStoreScanState,CStoreScanState继承于ScanState。具体定义代码如下:

      typedef struct CStoreScanState : ScanState {
      Relation ss_currentDeltaRelation;
      Relation ss_partition_parent;
      TableScanDesc ss_currentDeltaScanDesc;
      bool ss_deltaScan;
      bool ss_deltaScanEnd;
      VectorBatch* m_pScanBatch;
      VectorBatch* m_pCurrentBatch;
      CStoreScanRunTimeKeyInfo* m_pScanRunTimeKeys;
      int m_ScanRunTimeKeysNum;
      bool m_ScanRunTimeKeysReady;
      CStore* m_CStore;
      CStoreScanKey csss_ScanKeys;
      int csss_NumScanKeys;
      bool m_fSimpleMap;
      bool m_fUseColumnRef;
      vecqual_func jitted_vecqual;
      bool m_isReplicaTable; *复制表标记符*/
      } CStoreScanState;

      CStoreScan算子的相关函数包括:ExecInitCStoreScan(初始化节点)、ExecCStoreScan(执行节点)、ExecEndCStoreScan(退出节点)、ExecReScanCStoreScan(重置节点)。

      ExecInitCStoreScan函数用于初始化CStoreScan算子。主要执行流程如下。

      (1) 创建并初始化CStoreScan算子,为节点创建表达式上下文。
      (2) 调用ExecAssignVectorForExprEval函数进行投影表达式的初始化。
      (3) 调用ExecInitResultTupleSlot函数和ExecInitScanTupleSlot函数分别初始化用于投影结果和用于扫描的slot。
      (4) 打开扫描表,调用ExecAssignResultTypeFromTL函数和ExecBuildVecProjectionInfo函数分别初始化结果扫描描述符和创建投影结构。

      ……(本节内容未完)

      为提升您的阅读体验,完整版内容已运用专业格式发布到CSDN·Gauss松鼠会专栏,请扫码下方二维码,“关注”后进行内容阅读或点击文末“阅读原文”进入博客进行学习~

      Gauss松鼠会
      汇集数据库从业人员及爱好者
      互助解决问题 共建数据库技术交流圈
      完整版内容,请点击“阅读原文”

      文章转载自Gauss松鼠会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论