当前位置: 首页 > news >正文

Postgresql源码(147)Nestloop流程与Instrumentation简单分析

nestloop流程

  1. 外层循环控制外表元组,内层循环控制内表元组。
  2. 每对外表/内表元组都要先过ON条件,再过WHERE条件。
  3. 支持参数化内表,outtertuple拿到后,把将外表的某些字段值传递给内表。
  4. 支持LEFT JOIN/SEMI JOIN/ANTI JOIN等多种连接类型。
  5. 通过InstrCountFiltered1/2统计被ON/WHERE条件过滤的元组数。
    • explain analyze中下面数据来源:
    • Rows Removed by Join Filter: 1234
    • Rows Removed by … Cond: 5678
  6. 每次返回一个符合条件的连接结果元组,直到所有外表元组处理完毕。

explain analyze打点位置:
在这里插入图片描述

1 nestloop流程简化

注意这套流程每次执行返回一条数据

ExecNestLoopfor (;;)if (需要新外表元组)取外表元组if (外表元组为空) return NULL处理参数,重扫内表取内表元组if (内表元组为空)标记需要新外表元组if (当前外表元组匹配了所有内标元组但没有匹配 && 是LEFT JOIN或ANTI JOIN)构造内表全NULL的连接结果if (通过WHERE条件)返回这个结果else统计被WHERE过滤if (ON连接条件通过)外表已匹配if (ANTI JOIN) 需要新外表,continueif (SEMI JOIN) 需要新外表if (WHERE通过) 返回否则计数else计数继续下一轮循环

2 nestloop源码分析

关键变量

  • NestLoopState *node:当前NestLoop节点的执行状态。
  • NestLoop *nl:计划树中的NestLoop节点。
  • PlanState *outerPlan, *innerPlan:外表和内表的执行节点。
  • TupleTableSlot *outerTupleSlot, *innerTupleSlot:外表和内表的当前元组。
  • ExprState *joinqual:连接条件(ON条件)。
  • ExprState *otherqual:其他条件(WHERE条件)。
  • ExprContext *econtext:表达式求值上下文。

源码:

static TupleTableSlot *
ExecNestLoop(PlanState *pstate)
{......for (;;){

获取外表元组,outer tuple

		if (node->nl_NeedNewOuter){outerTupleSlot = ExecProcNode(outerPlan);if (TupIsNull(outerTupleSlot)){// 外表没数据,结束。return NULL;}// 处理NestLoop参数foreach(lc, nl->nestParams){......

将外表的某些字段值传递给内表

				innerPlan->chgParam = bms_add_member(innerPlan->chgParam,paramno);}/** now rescan the inner plan*/

重新扫描内表

			ExecReScan(innerPlan);}

获取内表元组(inner tuple)

		innerTupleSlot = ExecProcNode(innerPlan);econtext->ecxt_innertuple = innerTupleSlot;

内表元组为空的处理

		if (TupIsNull(innerTupleSlot)){

需要新外表元组

			node->nl_NeedNewOuter = true;

处理LEFT JOIN/ANTI JOIN的特殊情况

到这里如果想进去,需要三个条件

  1. 外表这一行,内表循环了多次,但是没有一次能连接上的,nl_MatchedOuter保持false
  2. 连接是left join 或 anti join

这两中连接方式,在连不上的时候需要特殊处理下:

			if (!node->nl_MatchedOuter &&(node->js.jointype == JOIN_LEFT ||node->js.jointype == JOIN_ANTI)){

没有匹配的内表元组,生成一个全NULL的内表元组

				econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;

检查WHERE条件

				if (otherqual == NULL || ExecQual(otherqual, econtext)){

连上一条,

					return ExecProject(node->js.ps.ps_ProjInfo);}elseInstrCountFiltered2(node, 1);}continue;}

内外表元组都不为空,判断连接条件

		if (ExecQual(joinqual, econtext)){node->nl_MatchedOuter = true;

ANTI JOIN特殊处理:匹配到就不返回

			if (node->js.jointype == JOIN_ANTI){node->nl_NeedNewOuter = true;continue;}

SEMI JOIN(single_match)只要第一个匹配

			if (node->js.single_match)node->nl_NeedNewOuter = true;

检查WHERE条件,满足即可返回

			if (otherqual == NULL || ExecQual(otherqual, econtext)){return ExecProject(node->js.ps.ps_ProjInfo);}else

计数,被WHERE条件过滤

				InstrCountFiltered2(node, 1);}else

计数,被ON连接条件过滤

			InstrCountFiltered1(node, 1);// 每次循环结束,重置表达式上下文,释放内存ResetExprContext(econtext);}
}

3 explain analyze记录逻辑

惯用法:

InstrStartNode
执行算子
InstrStopNode

源码:

void
InstrStartNode(Instrumentation *instr)
{...

记录启动时间

	INSTR_TIME_SET_CURRENT_LAZY(instr->starttime); 记录启动时间...
}/* Exit from a plan node */
void
InstrStopNode(Instrumentation *instr, double nTuples)
{double		save_tuplecount = instr->tuplecount;instr_time	endtime;/* count the returned tuples */instr->tuplecount += nTuples;

记录本次算子执行时间

	if (instr->need_timer){INSTR_TIME_IS_ZERO(instr->starttime);INSTR_TIME_SET_CURRENT(endtime);INSTR_TIME_ACCUM_DIFF(instr->counter, endtime, instr->starttime);INSTR_TIME_SET_ZERO(instr->starttime);}...if (!instr->running){instr->running = true;

记录第一次算子执行完的时间,无论算子是否返回数据

		instr->firsttuple = INSTR_TIME_GET_DOUBLE(instr->counter);}else{if (instr->async_mode && save_tuplecount < 1.0)instr->firsttuple = INSTR_TIME_GET_DOUBLE(instr->counter);}
}
http://www.lryc.cn/news/589452.html

相关文章:

  • python实现自动化sql布尔盲注(二分查找)
  • 03 51单片机之独立按键控制LED状态
  • 论文 视黄素与细胞修复
  • 小型客厅如何装修设计?
  • 微信小程序开发-桌面端和移动端UI表现不一致问题记录
  • [ROS 系列学习教程] ROS动作通讯(Action):通信模型、Hello World与拓展
  • Linux操作系统之信号:保存与处理信号
  • Quick API:赋能能源行业,化解数据痛点
  • MongoDB 学习笔记
  • 三年高端随身WIFI销量冠军如何用“技术普惠”击穿行业暴利,用户直呼真香;随身WIFI哪个牌子性价比最高?
  • netstat -tlnp | grep 5000
  • 基于Python的图像文字识别系统
  • Linux探秘坊-------14.信号
  • ObservableValidator自定义错误样式
  • 从知识变现到创始人IP:知识付费的进阶之路
  • zip 加密压缩文件的原理是什么?
  • 【Java】【企业级应用】学生信息管理系统项目介绍
  • 算法入门:BFS与DFS详解(C++实现)
  • 【kubernetes】--ConfigMap
  • 极致cms多语言建站|设置主站默认语言与设置后台固定语言为中文
  • frp内网穿透(二)
  • 牛客:HJ20 密码验证合格程序[华为机考][字符串]
  • 一般芯片电气特性中Flash参数达到其最大值的条件是什么?
  • 【人工智能99问】激活函数有哪些,如何选择使用哪个激活函数?(5/99)
  • 全新 Python 项目托管到 Gitee 私有仓库完整流程(带详细命令注释)
  • 【PTA数据结构 | C语言版】构造二叉树
  • 软件质量概述
  • 使用 pdb 来 debug 调试 python 程序
  • I3C通信驱动开发注意事项
  • Linux715 磁盘管理:逻辑卷