openGauss向量化引擎--hash join

2022-02-09 08:36:53 浏览数 (1)

openGauss向量化引擎--hash join

传统的行执行器采用一次一个元组的执行模式,执行过程中CPU大部分时间没有用了处理数据,都用在了遍历执行树等操作,导致CPU的有效利用率较低。面向OLAP场景大量函数调用次数,需要巨大开销,为解决次问题,openGauss中开发了向量化引擎。采用一次一批元组的执行模式,可大幅减少遍历执行节点及调用函数的开销。

本文主要介绍hash join如何进行向量化的。

算子之间数据传递结构

算子之间数据组织及传递结构是VectorBatch:

代码语言:javascript复制
class VectorBatch : public BaseObject {
public:
    // number of rows in the batch.
    int m_rows;
    // number of columns in the batch.
    int m_cols;
    // Shall we check the selection vector.
    bool m_checkSel;
    // Selection vector;
    bool* m_sel;
    // ScalarVector
    ScalarVector* m_arr;
    // SysColumns
    SysColContainer* m_sysColumns;
    // Compress buffer
    StringInfo m_pCompressBuf;
...
}

主要由3个数组在表示:m_vals为列值数组,m_flag为对应列的行值是否为NULL,m_sel为该行是否满足过滤条件。

VecHashJoin

向量化hash join的算子是VecHashJoin。其执行函数是ExecVecHashJoin,分为2个阶段:HASH_BUILD和HASH_PROBE。

代码语言:javascript复制
VectorBatch* ExecVecHashJoin(VecHashJoinState* node)
{
    int64 rows = 0;
    for (;;) {
        switch (node->joinState) {
            case HASH_BUILD: {
        if (node->hashTbl == NULL)
          node->hashTbl = New(CurrentMemoryContext) HashJoinTbl(node);
        ((HashJoinTbl*)(node->hashTbl))->Build();//构建hash表
        rows = ((HashJoinTbl*)(node->hashTbl))->getRows();
 
            } break;
            case HASH_PROBE: {
                result = ((HashJoinTbl*)(node->hashTbl))->Probe();//进行hash探测并构建join结果
                return result;
            }
            default:
                break;
        }
    }
}

HASH_BUILD

其中build的阶段又分为2个小阶段:1)获取内表的batch,然后通过m_funBuild:申请hashCell *cell_arr连续内存,每个节点是一个hashCell,大小是m_cellSize,共有batch中记录的行数个。然后将其接入m_cache链表。然后将batch中列值依次存入cell_arr中。2)通过PrepareProbe函数构建Hash表,并将cell_arr中值放到hash表中。

代码语言:javascript复制
void HashJoinTbl::Build()
{
  for (;;) {
    batch = VectorEngine(inner_node);//获取内表batch
    if (unlikely(BatchIsNull(batch)))
        break;
    RuntimeBinding(m_funBuild, m_strategy)(batch);
  }
  PushDownFilterIfNeed();
  PrepareProbe();
  ...
}

第1阶段:

第2阶段:

第2阶段,通过m_keyIdx数组得到哪一列是join key,将cell_arr中该列值拿出来通过m_innerHashFuncs函数计算hash值,将其保存到m_cacheLoc[]数组中,作为m_data数组下标,通过这种方式将内表列值放到hash表中。

HASH_PROBE

通过probeHashTable进行探测,并join。也分为2个小阶段:1)外表hash阶段:首先获取外表的batch,通过m_outerHashFuncs hash函数将外表的join key列hash出的值放到m_cacheLoc数组中,作为hash表数组的下标:m_hashTbl->m_data[m_cacheLoc[i]]。2)join阶段:定位到的m_hashTbl->m_data[m_cacheLoc[i]]中列值和外表中列值是否相同,若相等则通过m_keyMatch[]数组标记。最后将m_keyMatch[]数组标记为1的列值构建成向量batch,并返回。

代码语言:javascript复制
VectorBatch* HashJoinTbl::probeHashTable(hashSource* probSource)
{
    VectorBatch* res_batch = NULL;
    while (true) {
        switch (m_probeStatus) {
            case PROBE_FETCH:
        //获取外表batch
                m_outRawBatch = probSource->getBatch();
                if (BatchIsNull(m_outRawBatch)) {
    
                } else {
                    int row = m_outRawBatch->m_rows;
                    int mask = m_hashTbl->m_size - 1;
hashBatch(m_outRawBatch, m_outKeyIdx, m_cacheLoc, m_outerHashFuncs);
          for (int i = 0; i < row; i  ) {
            m_cacheLoc[i] = m_outRawBatch->m_arr[icol].m_vals[i] & mask;
            m_cellCache[i] = m_hashTbl->m_data[m_cacheLoc[i]];
            m_match[i] = false; /* flag all the row no match */
            m_keyMatch[i] = true;
          }
          ...
                }
                break;
            case PROBE_DATA:
                res_batch = (this->*m_joinFun)(m_outRawBatch);
                if (!BatchIsNull(res_batch))
                    return res_batch;
                break;

            case PROBE_FINAL:
                return endJoin();
            default:
                break;
        }
    }
}
//
VectorBatch* HashJoinTbl::innerJoinT(VectorBatch* batch)//外部batch
{
  while (m_doProbeData) {
        last_build_idx = 0;
    RuntimeBinding(m_matchKeyFunction, i)(&batch->m_arr[m_outKeyIdx[i]], row, m_keyIdx[i], i);
    for (row_idx = last_build_idx; row_idx < row; row_idx  ) {
      if (m_keyMatch[row_idx]) {
        val = m_cellCache[row_idx]->m_val;
        for (i = 0; i < m_innerBatch->m_cols; i  ) {
                    p_vector = &m_innerBatch->m_arr[i];

                    p_vector->m_vals[result_row] = val[i].val;
                    p_vector->m_flag[result_row] = val[i].flag;
                }

                for (i = 0; i < m_outerBatch->m_cols; i  ) {
                    p_vector = &m_outerBatch->m_arr[i];
                    p_vector->m_vals[result_row] = batch->m_arr[i].m_vals[row_idx];
                    p_vector->m_flag[result_row] = batch->m_arr[i].m_flag[row_idx];
                }
                result_row  ;
        
      }
    }
  }
  return buildResult(m_innerBatch, m_outerBatch, true);
}

0 人点赞