【PostgreSQL内核学习(二十一)—— 执行器(InitPlan)】

这篇具有很好参考价值的文章主要介绍了【PostgreSQL内核学习(二十一)—— 执行器(InitPlan)】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 postgresql-10.1 的开源代码和《OpenGauss数据库源码解析》和《PostgresSQL数据库内核分析》一书

概述

  在【OpenGauss源码学习 —— 执行器(execMain)】一文中,我们学习了执行器中执行查询的核心函数和逻辑。在本文中,我们将深入研究 InitPlan 的内容。InitPlan 是数据库查询执行过程中的关键组成部分,它用于在查询计划的执行过程中初始化子查询或表达式的结果,以确保正确的查询执行顺序和结果。通过本文的学习,我们将深入了解 InitPlan 的原理和实现细节,从而更好地理解数据库查询的执行机制。具体学习的模块如下图红色框体所示:
【PostgreSQL内核学习(二十一)—— 执行器(InitPlan)】,PostgerSQL,postgresql,数据库

InitPlan 函数

  InitPlan 函数是数据库查询执行过程中的初始化函数,其作用和功能主要集中在准备和设置查询计划(Query Plan)的执行环境。在关系型数据库管理系统(如 PostgreSQL)中,这是执行数据库查询的关键步骤。函数的主要职责包括:

  • 权限验证检查对于涉及的表是否有足够的访问权限,确保查询的安全性。
  • 设置执行状态初始化查询的执行状态(EState),包括设置查询涉及的表(range table)和计划语句(PlannedStmt)。
  • 处理结果关系对数据库操作影响的表(如插入更新删除操作的目标表)进行初始化和锁定。这包括处理分区表的情况,确保正确处理表的分区结构。
  • 锁定更新/共享关系对于涉及 FOR UPDATEFOR SHARE 的表,提前进行锁定,以避免在执行计划树初始化过程中的锁升级。
  • 初始化元组表创建和初始化元组表,这是用于在查询执行过程中临时存储和处理数据的结构。
  • 子计划的初始化对查询计划中的所有子计划(子查询)进行初始化,设置它们的私有状态。这对于处理复杂查询中的嵌套子查询非常关键。
  • 初始化查询计划树的节点对查询计划树中的每个节点进行初始化,这是查询执行的核心部分,涉及打开文件分配资源等准备工作。
  • 设置垃圾过滤器对于 SELECT 查询,如果目标列表中包含“垃圾”属性(即不需要的数据),则初始化垃圾过滤器,以清理这些不需要的数据。

  具体操作如下:

  • 函数首先接收一个 QueryDesc 类型的参数,这包含了操作类型计划的语句执行状态等信息。
  • 根据计划语句操作类型,函数进行不同的初始化操作,这可能包括打开文件分配内存锁定表等。
  • 函数处理可能存在的子计划,并初始化它们。这是重要的,因为子计划可能会影响主查询的执行。
  • 最后,函数准备好执行计划树中的所有节点,以便执行实际的数据库查询。

  总的来说,InitPlan 函数是数据库查询执行的重要部分,负责准备和初始化所有必要的结构和状态,以便数据库管理系统可以有效地执行一个查询计划。函数源码如下所示:(路径:src\backend\executor\execMain.c

/* ----------------------------------------------------------------
 *        InitPlan
 *
 *        初始化查询计划: 打开文件、分配存储空间并启动规则管理器
 * ----------------------------------------------------------------
 */
static void
InitPlan(QueryDesc *queryDesc, int eflags)
{
    CmdType     operation = queryDesc->operation; // 获取操作类型,例如 SELECT、UPDATE 等
    PlannedStmt *plannedstmt = queryDesc->plannedstmt; // 获取已计划的语句
    Plan       *plan = plannedstmt->planTree; // 获取计划的查询树
    List       *rangeTable = plannedstmt->rtable; // 获取涉及的范围表(关系表)列表
    EState     *estate = queryDesc->estate; // 获取执行状态
    PlanState  *planstate; // 声明用于后续初始化的 PlanState 指针
    TupleDesc  tupType; // 声明用于描述元组的结构
    ListCell   *l; // 声明用于列表迭代的 ListCell 指针
    int         i; // 声明用于迭代的整型变量

    /* 执行权限检查 */
    ExecCheckRTPerms(rangeTable, true); // 检查对涉及的表的访问权限

    /* 初始化节点的执行状态 */
    estate->es_range_table = rangeTable; // 设置执行状态中的范围表
    estate->es_plannedstmt = plannedstmt; // 设置执行状态中的已计划语句

    /* 初始化结果关系,打开和锁定结果关联 */
    if (plannedstmt->resultRelations)
    {
        List       *resultRelations = plannedstmt->resultRelations; // 获取结果关系列表
        int         numResultRelations = list_length(resultRelations); // 计算结果关系的数量
        ResultRelInfo *resultRelInfos; // 声明结果关系信息数组
        ResultRelInfo *resultRelInfo; // 声明单个结果关系信息

        /* 为结果关系信息数组分配内存 */
        resultRelInfos = (ResultRelInfo *)
            palloc(numResultRelations * sizeof(ResultRelInfo));
        resultRelInfo = resultRelInfos;
        foreach(l, resultRelations)
        {
            Index       resultRelationIndex = lfirst_int(l); // 获取结果关系索引
            Oid         resultRelationOid; // 声明结果关系的 OID
            Relation    resultRelation; // 声明结果关系

            /* 获取结果关系的 OID,并打开结果关系 */
            resultRelationOid = getrelid(resultRelationIndex, rangeTable);
            resultRelation = heap_open(resultRelationOid, RowExclusiveLock);

            /* 初始化单个结果关系信息 */
            InitResultRelInfo(resultRelInfo,
                              resultRelation,
                              resultRelationIndex,
                              NULL,
                              estate->es_instrument);
            resultRelInfo++;
        }
        estate->es_result_relations = resultRelInfos; // 设置执行状态中的结果关系信息数组
        estate->es_num_result_relations = numResultRelations; // 设置执行状态中的结果关系数量
        estate->es_result_relation_info = NULL; // 初始化结果关系信息为 NULL

	    /* 对分区结果关系进行处理 */
	    estate->es_root_result_relations = NULL; // 初始化根结果关系为 NULL
	    estate->es_num_root_result_relations = 0; // 初始化根结果关系数量为 0
	    if (plannedstmt->nonleafResultRelations)
	    {
	        int         num_roots = list_length(plannedstmt->rootResultRelations); // 获取根结果关系的数量
	
	        /* 为分区表的根建立 ResultRelInfos */
	        resultRelInfos = (ResultRelInfo *)
	            palloc(num_roots * sizeof(ResultRelInfo)); // 分配内存
	        resultRelInfo = resultRelInfos;
	        foreach(l, plannedstmt->rootResultRelations)
	        {
	            Index       resultRelIndex = lfirst_int(l); // 获取结果关系索引
	            Oid         resultRelOid; // 声明结果关系的 OID
	            Relation    resultRelDesc; // 声明结果关系描述
	
	            /* 获取结果关系的 OID 并打开结果关系描述 */
	            resultRelOid = getrelid(resultRelIndex, rangeTable);
	            resultRelDesc = heap_open(resultRelOid, RowExclusiveLock);
	
	            /* 初始化根结果关系信息 */
	            InitResultRelInfo(resultRelInfo,
	                              resultRelDesc,
	                              lfirst_int(l),
	                              NULL,
	                              estate->es_instrument);
	            resultRelInfo++;
	        }
	
	        estate->es_root_result_relations = resultRelInfos; // 设置执行状态中的根结果关系信息
	        estate->es_num_root_result_relations = num_roots; // 设置执行状态中的根结果关系数量
	
	        /* 锁定非叶结果关系 */
	        foreach(l, plannedstmt->nonleafResultRelations)
	        {
	            Index       resultRelIndex = lfirst_int(l);
	
	            /* 对不在根结果关系列表中的关系加锁 */
	            if (!list_member_int(plannedstmt->rootResultRelations,
	                                 resultRelIndex))
	                LockRelationOid(getrelid(resultRelIndex, rangeTable),
	                                RowExclusiveLock);
	        }
	    }
	}
	else
	{
	    /* 如果没有结果关系,则相应地设置状态 */
	    estate->es_result_relations = NULL;
	    estate->es_num_result_relations = 0;
	    estate->es_result_relation_info = NULL;
	    estate->es_root_result_relations = NULL;
	    estate->es_num_root_result_relations = 0;
	}
	
	/*
	 * 类似地,在初始化计划树之前,我们需要锁定被选为 FOR [KEY] UPDATE/SHARE 的关系,
	 * 否则可能会有锁升级的风险。同时,构建 ExecRowMark 列表。
	 * 这里忽略了分区的子表(因为 isParent=true),它们将被第一个引用它们的 Append 或 MergeAppend 节点锁定。
	 * (注意,对应于分区子表的 RowMarks 与其它的在同一个列表中,即 plannedstmt->rowMarks。)
	 */
	estate->es_rowMarks = NIL; // 初始化执行状态中的行标记列表为 NIL
	foreach(l, plannedstmt->rowMarks) // 遍历行标记
	{
		PlanRowMark *rc = (PlanRowMark *) lfirst(l); // 获取当前行标记
		Oid			relid; // 声明关系的 OID
		Relation	relation; // 声明关系
		ExecRowMark *erm; // 声明执行行标记

		/* 忽略父级行标记,因为它们在运行时不相关 */
		if (rc->isParent)
			continue;

		/* 获取关系的 OID(如果是子查询则为 InvalidOid) */
		relid = getrelid(rc->rti, rangeTable);

		/* 根据标记类型锁定关系 */
		switch (rc->markType)
		{
			case ROW_MARK_EXCLUSIVE:
			case ROW_MARK_NOKEYEXCLUSIVE:
			case ROW_MARK_SHARE:
			case ROW_MARK_KEYSHARE:
				relation = heap_open(relid, RowShareLock); // 使用 RowShareLock 打开关系
				break;
			case ROW_MARK_REFERENCE:
				relation = heap_open(relid, AccessShareLock); // 使用 AccessShareLock 打开关系
				break;
			case ROW_MARK_COPY:
				/* 不需要访问物理表 */
				relation = NULL;
				break;
			default:
				elog(ERROR, "unrecognized markType: %d", rc->markType); // 无法识别的 markType
				relation = NULL;	/* 保持编译器安静 */
				break;
		}

		/* 检查关系是否是合法的标记目标 */
		if (relation)
			CheckValidRowMarkRel(relation, rc->markType);

		erm = (ExecRowMark *) palloc(sizeof(ExecRowMark)); // 分配执行行标记内存
		erm->relation = relation; // 设置关系
		erm->relid = relid; // 设置关系 ID
		erm->rti = rc->rti; // 设置关系表索引
		erm->prti = rc->prti; // 设置父关系表索引
		erm->rowmarkId = rc->rowmarkId; // 设置行标记 ID
		erm->markType = rc->markType; // 设置标记类型
		erm->strength = rc->strength; // 设置强度
		erm->waitPolicy = rc->waitPolicy; // 设置等待策略
		erm->ermActive = false; // 设置为非激活状态
		ItemPointerSetInvalid(&(erm->curCtid)); // 设置当前 TID 为无效
		erm->ermExtra = NULL; // 设置额外信息为 NULL
		estate->es_rowMarks = lappend(estate->es_rowMarks, erm); // 将执行行标记添加到列表中
	}

	/* 初始化执行器的元组表为空 */
	estate->es_tupleTable = NIL; // 设置元组表为 NIL
	estate->es_trig_tuple_slot = NULL; // 设置触发器元组槽为 NULL
	estate->es_trig_oldtup_slot = NULL; // 设置旧触发器元组槽为 NULL
	estate->es_trig_newtup_slot = NULL

	/* 标记 EvalPlanQual 为非活动状态 */
	estate->es_epqTuple = NULL; // 设置 EPQ 元组为 NULL
	estate->es_epqTupleSet = NULL; // 设置 EPQ 元组集为 NULL
	estate->es_epqScanDone = NULL; // 设置 EPQ 扫描完成标志为 NULL
	
	/*
	 * 为每个子计划初始化私有状态信息。在执行主查询树的 ExecInitNode 之前必须做这个,
	 * 因为 ExecInitSubPlan 期望能找到这些条目。
	 */
	Assert(estate->es_subplanstates == NIL); // 断言确保子计划状态列表为空
	i = 1;						/* 子计划索引从 1 开始 */
	foreach(l, plannedstmt->subplans) // 遍历所有子计划
	{
		Plan	   *subplan = (Plan *) lfirst(l); // 获取当前子计划
		PlanState  *subplanstate; // 声明子计划状态
		int			sp_eflags; // 声明子计划的执行标志
	
		/*
		 * 子计划不需要执行向后扫描或标记/恢复。如果是无参数子计划(非 initplan),
		 * 我们建议它准备好高效处理 REWIND;否则就没必要了。
		 */
		sp_eflags = eflags
			& (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA); // 设置子计划执行标志
		if (bms_is_member(i, plannedstmt->rewindPlanIDs))
			sp_eflags |= EXEC_FLAG_REWIND; // 如果子计划需要 REWIND,添加标志
	
		subplanstate = ExecInitNode(subplan, estate, sp_eflags); // 初始化子计划状态
	
		estate->es_subplanstates = lappend(estate->es_subplanstates,
										   subplanstate); // 将子计划状态添加到列表
	
		i++; // 子计划索引递增
	}
	
	/*
	 * 为查询树中的所有节点初始化私有状态信息。这将打开文件、分配存储空间,
	 * 并准备开始处理元组。
	 */
	planstate = ExecInitNode(plan, estate, eflags); // 初始化主查询树的节点
	
	/*
	 * 获取描述返回元组类型的元组描述符。
	 */
	tupType = ExecGetResultType(planstate); // 获取结果类型的元组描述符
	
	/*
	 * 如有必要,初始化垃圾过滤器。如果顶层目标列表中有任何垃圾属性,
	 * SELECT 查询就需要一个过滤器。
	 */
	if (operation == CMD_SELECT)
	{
		bool		junk_filter_needed = false; // 声明是否需要垃圾过滤器的标志
		ListCell   *tlist; // 声明目标列表的迭代器
	
		foreach(tlist, plan->targetlist) // 遍历目标列表
		{
			TargetEntry *tle = (TargetEntry *) lfirst(tlist); // 获取当前目标项
	
			if (tle->resjunk) // 如果是垃圾属性
			{
				junk_filter_needed = true; // 标记需要垃圾过滤器
				break;
			}
		}
	
		if (junk_filter_needed) // 如果需要垃圾过滤器
		{
			JunkFilter *j; // 声明垃圾过滤器
	
			j = ExecInitJunkFilter(planstate->plan->targetlist,
								   tupType->tdhasoid,
								   ExecInitExtraTupleSlot(estate)); // 初始化垃圾过滤器
			estate->es_junkFilter = j; // 设置执行状态中的垃圾过滤器
			/* 期望返回清理后的元组类型 */
			tupType = j->jf_cleanTupType; // 设置清理后的元组类型
		}
	}
	
	queryDesc->tupDesc = tupType; // 设置查询描述的元组描述符
	queryDesc->planstate = planstate; // 设置查询描述的计划状态
}

代码段解释

PlanRowMark *rc = (PlanRowMark *) lfirst(l); // 获取当前行标记

  这行代码的作用是从一个链表结构中提取出当前遍历到的元素,并将其转换为 PlanRowMark 类型的指针。这里,PlanRowMark 是一个结构体,用于存储与特定数据库表或查询中的行相关的标记信息。这种标记通常用于数据库查询计划中,以指示对特定行的特定操作或限制。

举个例子说明

  假设你正在执行一个数据库查询,该查询包括对多个表的操作,并且一些操作需要特别标记(例如,更新或删除某些特定行时需要加锁)。在这种情况下,PlanRowMark 结构体将用于存储这些操作的相关信息。
  例如,你有一个查询计划,它包含对两个表的操作:表 A 和表 B。在这个计划中,你想对表 A 中的某些行加上“FOR UPDATE”锁,而对表 B 中的某些行加上“FOR SHARE”锁。这样的锁定操作可以防止其他事务在当前事务完成之前修改或删除这些行。
  在执行这个计划的初始化过程中,会创建一个包含 PlanRowMark 元素的列表,每个元素代表一个需要特殊处理的表及其行。当遍历到这个列表中的一个元素时,这行代码就会从列表中提取出一个 PlanRowMark 结构体,其中包含了如何处理与表 A 或表 B 相关行的信息。例如,对于表 A 的行,这个结构体可能会指示这些行应该被加上“FOR UPDATE”锁。


ExecInitNode 函数

  ExecInitNode 函数是数据库查询执行过程中的一个关键函数用于递归初始化查询计划树中的所有节点。它根据不同类型的节点(如扫描节点连接节点聚合节点等)执行相应的初始化操作。
  ExecInitNode 函数对查询计划中的每个节点进行递归初始化。它根据节点类型(如结果节点扫描节点连接节点等)调用相应的初始化函数。这些函数为每种类型的节点设置特定的执行状态和行为。

  • 节点初始化对于每种节点类型(如结果节点、聚合节点、扫描节点等),函数通过调用特定的初始化函数(如 ExecInitResultExecInitSeqScan)来准备节点的执行。这包括为节点设置运行时所需的资源、数据结构和状态。
  • 检查栈深度为了避免栈溢出,函数在开始节点的初始化之前检查栈深度。
  • 初始化子计划对于包含子计划的节点(如子查询),函数初始化这些子计划,并将它们的状态连接到主节点的状态上。
  • 设置性能监控如果启用了性能监控,函数将为节点分配监控工具。

  通过这种方式,ExecInitNode 确保查询计划中的每个节点都被正确初始化,并且具备执行查询所需的全部信息和资源。这个过程是查询执行的基础,它确保了当查询被执行时,每个节点都能按照预期的逻辑和顺序工作,从而有效地完成整个查询任务。函数源码如下所示:(路径:src\backend\executor\execProcnode.c

/* ------------------------------------------------------------------------
 *      ExecInitNode
 *
 *      递归初始化查询计划树的根节点及其子节点。
 *
 *      输入参数:
 *        'node' 是由查询规划器产生的计划树中的当前节点
 *        'estate' 是计划树的共享执行状态
 *        'eflags' 是在 executor.h 中描述的标志位的位或运算结果
 *
 *      返回一个与给定计划节点对应的 PlanState 节点。
 * ------------------------------------------------------------------------
 */
PlanState *
ExecInitNode(Plan *node, EState *estate, int eflags)
{
    PlanState  *result; // 声明返回的 PlanState 结构体
    List       *subps; // 子计划列表
    ListCell   *l; // 用于遍历列表的临时变量

    /* 若节点为 NULL,则到达叶子节点的末端,无需进一步处理 */
	if (node == NULL)
		return NULL;
		
	/* 确保有足够的栈空间可用。需要在这里以及 ExecProcNode()(通过 ExecProcNodeFirst())中检查,以确保在初始化节点树时不会超过栈深度。 */
	check_stack_depth();
	
	/* 根据节点类型选择相应的初始化函数 */
	switch (nodeTag(node))
	{
	    /* 控制节点 */
	    case T_Result:
	        result = (PlanState *) ExecInitResult((Result *) node, estate, eflags);
	        break;
	    /* ...其他节点类型的处理... */
	
	    /* 扫描节点 */
	    case T_SeqScan:
	        result = (PlanState *) ExecInitSeqScan((SeqScan *) node, estate, eflags);
	        break;
	    /* ...其他节点类型的处理... */
	
	    /* 连接节点 */
	    case T_NestLoop:
	        result = (PlanState *) ExecInitNestLoop((NestLoop *) node, estate, eflags);
	        break;
	    /* ...其他节点类型的处理... */
	
	    /* 材料化节点 */
	    case T_Material:
	        result = (PlanState *) ExecInitMaterial((Material *) node, estate, eflags);
	        break;
	    /* ...其他节点类型的处理... */
	
	    /* 默认情况下,报告无法识别的节点类型错误 */
	    default:
	        elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
	        result = NULL;  /* 避免编译器警告 */
	        break;
	}
	
	/* 添加一个包装器,用于在第一次执行时检查栈深度 */
	result->ExecProcNodeReal = result->ExecProcNode;
	result->ExecProcNode = ExecProcNodeFirst;
	
	/* 初始化节点中存在的任何初始化计划(initPlans)。规划器将它们放在一个单独的列表中。 */
	subps = NIL;
	foreach(l, node->initPlan)
	{
	    SubPlan    *subplan = (SubPlan *) lfirst(l);
	    SubPlanState *sstate;
	
	    Assert(IsA(subplan, SubPlan));
	    sstate = ExecInitSubPlan(subplan, result);
	    subps = lappend(subps, sstate);
	}
	result->initPlan = subps;
	
	/* 如果请求,为该节点设置监控工具 */
	if (estate->es_instrument)
	    result->instrument = InstrAlloc(1, estate->es_instrument);
	
	return result;
}

总结

  ExecutorStart 在查询执行阶段之前调用,作为整个执行过程的入口点。它负责触发 InitPlan,然后继续进行查询执行的后续步骤。在 ExecutorStart 中,首先调用 InitPlan 来完成查询计划的初始化工作,然后设置好执行环境,准备好所有必要的数据结构和状态。这包括分配内存准备执行环境等。
  InitPlan 是数据库查询执行过程中的一个重要函数,主要负责初始化查询计划(Query Plan)。在执行数据库查询之前InitPlan 函数设置和准备了所有必要的执行状态,包括但不限于权限检查结果关系的初始化锁定子计划的初始化。此外,它还负责为各种节点类型(如扫描节点连接节点等)设置相应的执行状态和行为。简而言之,InitPlan 确保了查询计划的各个组成部分都准备就绪,可以进行高效的查询执行。文章来源地址https://www.toymoban.com/news/detail-793927.html

到了这里,关于【PostgreSQL内核学习(二十一)—— 执行器(InitPlan)】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 造个轮子-任务调度执行小框架-任务清单执行器实现

    okey,上一篇文章我们提到了,如何实现它的一个清单的一个代理。这里的话我们来捋一捋我们的这个执行流程是啥: 所以的话,我们的我们这里今天要做的是这个执行器的一个执行。当然这里的话,我们也是分两个部分,因为这个执行器的话,是分两个部分的,一个是正常的

    2024年02月13日
    浏览(45)
  • Junit执行器Runner探索之旅

    单元测试是每个程序员必备的技能,而Runner是每个单元测试类必有属性。本文通过解读Junit源码,介绍junit中每个执行器的使用方法,让读者在单元测试时,可以灵活的使用Runner执行器。 在今年的敏捷团队建设中,京东物流通过Suite执行器实现了一键自动化单元测试。Juint除了

    2024年02月08日
    浏览(38)
  • PgSQL-执行器机制-Unique算子

    PgSQL-执行器机制-Unique算子 PgSQL中输出去重的元组有多种方法,比如通过HashAgg或者GroupAgg。这里我们介绍第三种方法,通过Unique算子来完成这个功能。当然语句上可以是:select distinct(id1) from t; 执行器执行算子的函数都是ExecXXX,其中XXX代表某个算子。Unique算子的执行是由函数

    2024年02月07日
    浏览(31)
  • 机械臂速成小指南(五):末端执行器

    👨‍🏫🥰🥳需要机械臂相关资源的同学可以在评论区中留言哦🤖😽🦄 指南目录📖: 🎉🎉机械臂速成小指南(零点五):机械臂相关资源🎉🎉 机械臂速成小指南(零):指南主要内容及分析方法 机械臂速成小指南(一):机械臂发展概况 机械臂速成小指南(二):

    2024年02月03日
    浏览(38)
  • Camunda 7.x 系列【53】Job 执行器

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo Job Executor 即任务执行器,是 Camunda 中的一个调度组件,负责执行异步后台作业。 Job 表示 Job Executor 执行的某一作业,例如,在定

    2024年02月09日
    浏览(38)
  • xxl-job执行器无法自动注册

    问题描述 在springboot项目里配置了xxl-job2.3.0,但是执行器无法自动注册 yaml配置如下: 执行器无法自动注册到xxl-job-admin 排查过程 经过debug发现,是spring没有加载xxlJobExecutor这个Bean debug流程(SpringApplication.run()–SpringApplication.refreshContext()–SpringApplication.refresh() --SpringApplication

    2024年02月16日
    浏览(33)
  • Spring Boot 中的任务执行器是什么,如何使用

    Spring Boot 是一个非常流行的 Java 开发框架,它的核心理念是通过简单的配置和约定来提高开发效率。在很多情况下,我们需要在后台执行一些任务,比如异步处理、定时任务等等。为了简化这些任务的开发和管理,Spring Boot 提供了一个任务执行器(Task Executor)。 任务执行器

    2024年02月15日
    浏览(32)
  • 【源码分析】XXL-JOB的执行器的注册流程

    目的:分析xxl-job执行器的注册过程 流程: 获取执行器中所有被注解( @xxlJjob )修饰的 handler 执行器注册过程 执行器中任务执行过程 版本: xxl-job 2.3.1 建议:下载 xxl-job 源码,按流程图 debug 调试, 看堆栈信息并按文章内容理解执行流程 。 完整流程图: 部分流程图: 首先启

    2023年04月22日
    浏览(44)
  • MYSQL04高级_逻辑架构剖析、查询缓存、解析器、优化器、执行器、存储引擎

    ①. 服务器处理客户端请求 ②. 连接层 系统(客户端)访问MySQL服务器前,做的第一件事就是建立TCP连接 经过三次握手建立连接成功后,MySQL服务器对TCP传输过来的账号密码做身份认证、权限获取 用户名或密码不对,会收到一个Access denied for user错误,客户端程序结束执行用户名密码认

    2024年02月12日
    浏览(38)
  • xxl-job中在分片的时候项目模块如何开启多个执行器?

    在xxl-job中,要在分片时开启多个执行器,您需要按照以下步骤进行操作: 1.在xxl-job的项目中,找到对应的模块(即需要开启多个执行器的模块)。 2.在模块的配置文件(通常是application.properties或application.yml)中,找到以下属性: properties xxl.job.executor.appname=   xxl.job.executor

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包