开发者
资源
openGauss开源代码评注——针对execJunk.cpp解析
openGauss开源代码评注——针对execJunk.cpp解析
新人帖
发表于2023/10/30
1.4k9

概述

execJunk.cpp文件实现了垃圾属性的处理逻辑。垃圾属性是一种特殊类型的属性,它们不会被打印、返回或存储在磁盘上,只在执行器内部使用。这些属性主要用于存储对执行器有用的信息,例如系统属性(比如ctid的值)或者不会被输出的排序键列。在这份代码中,垃圾属性通过TargetEntry节点的resjunk字段来标识。

该文件中包含了多个函数,用于初始化垃圾过滤器(ExecInitJunkFilterExecInitJunkFilterConversion)、查找垃圾属性的值(ExecFindJunkAttributeExecFindJunkAttributeInTlist)以及从元组中过滤垃圾属性(ExecFilterJunkBatchExecFilterJunk)。其中BatchExecFilterJunk用于批处理操作中的垃圾属性过滤,而BatchCheckNodeIdentifier用于验证批处理操作中的节点标识符。

此外,还有一个函数ExecSetjunkFilteDescriptor用于设置垃圾过滤器结果插槽的元组描述符,以匹配新的元组描述符。文件中的注释提供了对每个函数的详细描述和用途。

需要注意的是,这份代码是一个数据库系统内部的实现,用于处理数据库查询和操作的底层逻辑。

源码链接

https://www.gitlink.org.cn/TerryTongJ/openGauss-server/tree/master/src/gausskernel/runtime/executor/execJunk.cpp?edit

技术细节解析

在该源码中,最核心的部分是垃圾属性的映射和过滤过程。在初始化垃圾过滤器时,会根据目标列表和元组类型构建属性映射关系。这个映射关系告诉系统在清理后的元组中,每个属性的位置。在过滤过程中,系统会根据映射关系,将需要的属性值从原始元组中复制到新的插槽中,从而构建一个不包含垃圾属性的元组。

另外,在批量处理时,系统会遍历批处理中的xc_node_id属性,确保它们与当前节点的标识符匹配。这个过程是为了保证在分布式系统中的数据一致性,防止数据被错误地更新或删除。

1.深入探讨ExecInitJunkFilter函数:垃圾属性过滤的内部机制

  • 背景

在执行器内部,元组的属性可以分为普通属性和垃圾属性。垃圾属性是不会被打印、返回或存储在磁盘上的属性。它们的唯一目的是存储一些仅对执行器有用的信息。垃圾属性是在执行计划生成阶段加入的,通常包含了一些系统需要的信息。

  • 函数功能

    • ExecInitJunkFilter函数的主要目的是初始化垃圾属性的过滤器。在这个函数中,我们传入了以下参数:
      • targetList`:查询目标列表,包含了查询计划中所有的目标属性。
      • hasoid:一个布尔值,指示结果中是否包含OID。
      • slot`:结果插槽,用于存储清理后的元组。
      • tam:表存储访问方法的类型,用于构建清理后的元组描述符。
  • 函数逻辑

  1. 清理元组描述符(cleanTupType):函数首先调用ExecCleanTypeFromTL函数,基于目标列表和hasoid参数,计算出清理后的元组描述符。这个描述符定义了清理后的元组的结构。
  2. 属性映射(cleanMap):然后,函数构建了一个属性映射数组cleanMap。这个数组的目的是将清理后的元组的属性与原始元组的属性进行映射,确保在后续操作中,可以正确地将属性值从原始元组中复制到新的插槽中。属性映射的逻辑在函数的后半部分实现。
  3. 创建结果插槽:函数根据清理后的元组描述符,创建了结果插槽。这个插槽将被用来存储清理后的元组。
  4. 属性映射逻辑:在构建属性映射数组时,函数遍历了目标列表。对于目标列表中的每个属性,如果它不是垃圾属性,就建立了属性映射关系。这个关系被记录在cleanMap数组中,确保了清理后的元组属性的顺序和原始元组中的属性顺序一致。

代码如下:

JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* slot, TableAmType tam)
{
    JunkFilter* junkfilter = NULL;  
    TupleDesc cleanTupType;  
    int cleanLength;  
    AttrNumber* cleanMap = NULL;  
    ListCell* t = NULL;  
    AttrNumber cleanResno;  

    // 计算清理后的元组描述符
    cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam);  

    // 设置槽的描述符,如果给定了槽,则使用给定的槽,否则创建一个新槽
    if (slot != NULL)
        ExecSetSlotDescriptor(slot, cleanTupType);
    else
        slot = MakeSingleTupleTableSlot(cleanTupType);

    cleanLength = cleanTupType->natts;  
    if (cleanLength > 0) {
        cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber));  
        cleanResno = 1;
        foreach (t, targetList) {
            TargetEntry* tle = (TargetEntry*)lfirst(t);
            
            // 如果不是 "junk" 属性,则建立属性映射关系
            if (!tle->resjunk) {
                cleanMap[cleanResno - 1] = tle->resno;
                cleanResno++;
            }
        }
    } else {
        cleanMap = NULL;
    }

    // 创建并初始化 JunkFilter 结构
    junkfilter = makeNode(JunkFilter);

    // 填充 JunkFilter 结构的字段
    junkfilter->jf_targetList = targetList;
    junkfilter->jf_cleanTupType = cleanTupType;
    junkfilter->jf_cleanMap = cleanMap;
    junkfilter->jf_resultSlot = slot;

    return junkfilter;
}

2.高效处理查询结果:优化数据库性能的关键策略

  • 背景

随着数据量的增加,数据库查询的复杂性也在不断提高。在查询结果中,通常会包含一些不需要的属性,这些属性被称为垃圾属性。处理大规模数据时,过滤掉这些垃圾属性可以显著提高查询性能。下面将介绍两个关键的函数,它们在数据库性能优化中发挥着重要作用。

  • ExecFilterJunk 函数的作用

ExecFilterJunk 函数的主要作用是根据垃圾属性映射,构建新的元组插槽,移除垃圾属性,并返回新的插槽。这个函数在 PostgreSQL 内部的查询执行过程中被广泛使用,确保只有必要的属性被保留,其他垃圾属性被过滤掉。

以下是该函数的核心代码片段:

TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
{
    // ... 从原始元组中提取所有属性值 ...
    
    // 转置数据到新元组的适当字段中
    for (i = 0; i < cleanLength; i++) {
        int j = cleanMap[i];

        if (j == 0) {
            // 移除垃圾属性
            values[i] = (Datum)0;
            isnull[i] = true;
        } else {
            // 保留必要属性
            values[i] = old_values[j - 1];
            isnull[i] = old_isnull[j - 1];
        }
    }

    // 返回过滤后的虚拟元组
    return ExecStoreVirtualTuple(resultSlot);
}
  • ExecFilterJunk 的优化策略

ExecFilterJunk 函数的主要任务是根据垃圾属性映射,构建新的元组插槽,移除垃圾属性,并返回新的插槽。为了优化这一过程,我们可以考虑以下策略:

  1. 智能属性选择: 在查询中只选择必要的属性,避免返回过多的列。通过优化查询语句,可以减少返回的属性数量,降低数据传输和处理的开销。
  2. 合理使用索引: 如果查询中涉及到的属性有索引,可以合理地使用索引来加速查询。索引可以提高查询的定位速度,减少数据扫描的范围,从而加速查询结果的生成。
  3. 定期清理无用属性: 在数据库设计中,定期审查数据表的设计,删除不再使用的属性。减少表的列数可以提高查询的效率,特别是在大规模数据的情况下。
  • BatchExecFilterJunk 函数的作用

BatchExecFilterJunk 函数是 ExecFilterJunk 的向量批处理版本。它通过转置数据到新元组的适当字段中,同时移除垃圾属性,从而提高了批处理数据的处理效率。这个函数在大规模数据处理中非常有效,确保了高性能的数据过滤。

以下是该函数的核心代码片段:

VectorBatch* BatchExecFilterJunk(_in_ JunkFilter* junkfilter, __inout VectorBatch* batch)
{
    // ... 获取垃圾属性的属性映射和元组描述符 ...
    
    // 转置数据到新元组的适当字段中
    for (i = 0; i < cleanLength; i++) {
        int j = cleanMap[i];

        if (j == 0) {
            for (int k = 0; k < columns[i].m_rows; k++) {
                columns[i].SetNull(k);  // 移除垃圾属性
            }
        } else {
            columns[i] = columns[j - 1];  // 保留必要属性
        }
    }

    // 返回修改后的批处理数据,列数不变
    return batch;
}
  • BatchExecFilterJunk 的优化策略

BatchExecFilterJunk 函数是 ExecFilterJunk 的向量批处理版本,专为大规模数据处理而设计。为了优化这一过程,我们可以考虑以下策略:

  1. 批处理操作: 将数据分批处理,减少单次处理的数据量。批处理可以降低内存占用,提高查询的处理速度。

  2. 并行处理: 对于大规模数据,可以考虑并行处理。将数据分成多个子任务,在多个处理器上并行执行,充分利用系统资源,提高处理效率。

  3. 内存管理: 合理管理内存,避免内存溢出和频繁的内存分配操作。使用合适的数据结构和算法,减少内存的使用,提高数据处理的效率。



3.保障分布式数据库数据一致性:PostgreSQL中的BatchCheckNodeIdentifier函数解析

  • 背景

在分布式数据库系统中,数据通常分布在多个节点上。当涉及到更新或删除操作时,需要确保这些操作只会在特定的节点上执行,以免引起数据不一致。BatchCheckNodeIdentifier函数就是为了解决这个问题而设计的。


  • 函数的作用

BatchCheckNodeIdentifier函数的主要作用是验证向量批处理中的xc_node_id垃圾属性,确保其值与当前节点标识符相匹配。这个验证过程是分布式数据库中确保数据一致性的一部分。如果xc_node_id的值与当前节点的标识符不匹配,这可能表明数据分布出现了问题,需要进行进一步的检查和修复。


  • 函数的实现

BatchCheckNodeIdentifier函数的实现非常巧妙。它通过遍历xc_node_id列向量中的值,与当前节点的标识符进行比较。如果发现不匹配的值,函数将抛出错误,阻止更新或删除操作的继续执行,从而确保了数据的一致性。

以下是该函数的关键代码片段:

void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch)
{
    ScalarVector* xc_node_id_col = NULL;  // 用于存储 xc_node_id 的列向量
    uint32 xc_node_id = 0;  // 存储当前 xc_node_id
    int counter = 0;  // 循环计数变量

    // 如果 xc_node_id 无效,则直接返回
    if (InvalidAttrNumber == junkfilter->jf_xc_node_id) {
        return;
    }

    // 获取 xc_node_id 列向量
    xc_node_id_col = &(batch->m_arr[junkfilter->jf_xc_node_id - 1]);

    // 遍历 xc_node_id 列向量中的值
    for (counter = 0; counter < xc_node_id_col->m_rows; counter++) {
        // 获取当前 xc_node_id 的值
        xc_node_id = DatumGetUInt32(xc_node_id_col->m_vals[counter]);
        
        // 检查当前 xc_node_id 是否与当前节点的标识不匹配,如果不匹配则抛出错误
        if (u_sess->pgxc_cxt.PGXCNodeIdentifier != xc_node_id) {
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                    errmsg("invalid node identifier for update/delete"),
                    errdetail("xc_node_id in batch is %u, while current node identifier is %u",
                        xc_node_id,
                        u_sess->pgxc_cxt.PGXCNodeIdentifier)));
        }
    }
}

总结

execJunk.cpp 文件是 PostgreSQL 数据库中的一部分,实现了垃圾属性(Junk Attribute)支持的相关功能。

  • ExecInitJunkFilter函数是PostgreSQL查询执行器中一个关键的组成部分,它负责创建并初始化垃圾过滤器,确保在执行计划的执行过程中,这些属性得到正确的处理。通过深入理解这个函数的内部机制,我们能够更好地理解PostgreSQL执行器的工作原理,为性能优化和查询执行过程的调优提供更多的可能性。

  • ExecFilterJunkBatchExecFilterJunk 函数都用于过滤掉垃圾属性,返回一个干净的、不包含垃圾属性的元组或者向量批处理,确保操作结果中不包含垃圾属性,使得数据处理更加高效和准确。ExecFilterJunk 适用于单个元组的处理,而 BatchExecFilterJunk 则适用于批量数据的处理。

  • BatchCheckNodeIdentifier函数是PostgreSQL分布式数据库系统中的关键组件,用于保障数据更新和删除操作的准确性和一致性。通过这个函数,PostgreSQL确保了分布式系统中数据的正确分发和处理,从而为用户提供了可靠的数据操作保障。

希望通过本篇博客的解析,读者能够更好地理解数据库性能优化的关键策略,同时能够对PostgreSQL分布式系统中的数据一致性验证有更深入的了解。

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞