openGauss内核分析:SQL by pass & 经典执行器
摘要:执行引擎一般负责查询的执行,执行引擎在SQL执行栈中起到接收优化器生成的执行计划Plan、并对通过存储引擎提供的数据读写接口,实现对数据进行计算得到查询的结果集。
本文分享自华为云社区《openGauss内核分析(七):SQL by pass & 经典执行器》,作者:Gauss松鼠会 。
执行引擎一般负责查询的执行,执行引擎在SQL执行栈中起到接收优化器生成的执行计划Plan、并对通过存储引擎提供的数据读写接口,实现对数据进行计算得到查询的结果集。

在典型的OLTP场景中,简单查询占了很大一部分比例。这种查询的特征是只涉及单表和简单表达式的查询,因此为了加速这类查询,openGauss提出了SQL by pass框架,在parse层对这类查询做简单的模式判别后,进入到特殊的执行路径里,跳过经典的执行器执行框架,包括算子的初始化与执行、表达式与投影等经典框架,直接重写一套简洁的执行路径,并且直接调用存储接口,这样可以大大加速简单查询的执行速度。
SQL by pass
enable_opfusion用于控制是否对简单增删改查进行优化,简单insert语句在开启enable_opfusion时的执行计划如下

由于开启SQL BY PASS,从exec_simple_query过来的语句,会判断可以走SQL BY PASS,否则进入CreatePortal走经典执行流程。
static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)
{
…
/* SQL bypass */
if (runOpfusionCheck) { // 进入SQL by pass
(void)MemoryContextSwitchTo(oldcontext);
void* opFusionObj = OpFusion::FusionFactory(
OpFusion::getFusionType(NULL, NULL, plantree_list), oldcontext, NULL, plantree_list, NULL);
if (opFusionObj != NULL) {
((OpFusion*)opFusionObj)->setCurrentOpFusionObj((OpFusion*)opFusionObj);
if (OpFusion::process(FUSION_EXECUTE, NULL, completionTag, isTopLevel, NULL)) {
CommandCounterIncrement();
finish_xact_command();
EndCommand(completionTag, dest);
MemoryContextReset(OptimizerContext);
break;
}
Assert(0);
}
(void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);
}
/*
* Create unnamed portal to run the query or queries in. If there
* already is one, silently drop it.
*/
portal = CreatePortal("", true, true); // 经典执行流程
…
}
进入InsertFusion::execute完成数据插入操作。
#0 InsertFusion::execute (this=0x7fd93a4104f8, max_rows=9223372036854775807, completionTag=0x7fd933e67020 "@p\346\063\331\177")
at opfusion_insert.cpp:297
#1 0x0000000001ac00d9 in OpFusion::fusionExecute (this=0x7fd93a4104f8, msg=0x0, completionTag=0x7fd933e67020 "@p\346\063\331\177",
isTopLevel=true, isQueryCompleted=0x0) at opfusion.cpp:453
#2 0x0000000001ac0389 in OpFusion::process (op=0, msg=0x0, completionTag=0x7fd933e67020 "@p\346\063\331\177", isTopLevel=true,
isQueryCompleted=0x0) at opfusion.cpp:491
#3 0x000000000193a910 in exec_simple_query (query_string=0x7fd966ad2060 "insert into t1 values(1,200);",
messageType=QUERY_MESSAGE, msg=0x7fd933e67210) at postgres.cpp:2624
SQL by pass适应的场景有:
- 只支持indexscan和indexonlyscan,且全部WHERE语句的过滤条件都在索引上。
- 只支持单表增删改查,不支持join、using。
- 只支持行存表,不支持分区表,表不支持有触发器。
- 不支持active sql、QPS等信息统计特性。
- 不支持正在扩容和缩容的表。
- 不支持查询或者修改系统列。
- 只支持简单SELECT语句,例如
SELECT c3 FROM t1 WHERE c1 = ? and c2 =10;
仅可以查询目标表的列,c1和c2列为索引列,后边可以是常量或者参数,可以使用 for update。
- 只支持简单INSERT语句,例如:
INSERT INTO t1 VALUES (?,10,?);
仅支持一个VALUES,VALUES里面的类型可以是常量和参数,不支持returning。
- 只支持简单DELETE语句,例如:
DELETE FROM t1 WHERE c1 = ? and c2 = 10;
c1和c2列为索引列,后边可以是常量或者参数。
- 只支持简单UPDATE语句,例如:
UPDATE t1 SET c3 = c3+? WHERE c1 = ? and c2 = 10;
c3列修改的值可以是常量和参数,也可以是一个简单的表达式,c1和c2列为索引列,后边可以是常量或者参数。
经典的执行器
关闭enable_opfusion,简单insert的执行计划是这样的

在这种执行流程中Portal是执行SQL语句的载体,每一条SQL对应唯一的Portal,不同的查询类型对应的Portal类型也有区别。
typedef enum PortalStrategy {
PORTAL_ONE_SELECT, // SQL语句包含单一的SELECT查询
PORTAL_ONE_RETURNING, // INSERT/UPDATE/DELETE语句包含Returning
PORTAL_ONE_MOD_WITH, // 查询语句包含With
PORTAL_UTIL_SELECT, // 工具类型查询语句,如explain
PORTAL_MULTI_QUERY // 所有其他类型查询语句
} PortalStrategy;
Portal的生命周期管理在exec_simple_query函数中实现,该函数负责Portal创建、执行和清理。Portal执行的主要执行流程包括PortalStart函数、PortalRun函数、PortalDrop函数几个部分。其中PortalStart函数负责进行Portal结构体初始化工作,包括执行算子初始化、内存上下文分配等;PortalRun函数负责真正的执行和运算,它是执行器的核心;PortalDrop函数负责最后的清理工作,主要是数据结构、缓存的清理。

PortalRun函数根据查询类型进入不同的处理函数
bool PortalRun(
Portal portal, long count, bool isTopLevel, DestReceiver* dest, DestReceiver* altdest, char* completionTag)
{
…
switch (portal->strategy) {
case PORTAL_ONE_SELECT:
…
case PORTAL_MULTI_QUERY: // insert从这里进入
PortalRunMulti(portal, isTopLevel, dest, altdest, completionTag);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
/* Always complete at end of RunMulti */
result = true;
break;
…
}
最终执行ExecInsertT完成数据插入。
#0 ExecInsertT (state=0x7fdbf1836060, slot=0x7fdbf0c86460, planSlot=0x7fdbf0c86460, estate=0x7fdbf0c74060, canSetTag=true,
options=0, partitionList=0x7fdbf3125860) at nodeModifyTable.cpp:800
#1 0x0000000001a684cd in ExecModifyTable (node=0x7fdbf1836060) at nodeModifyTable.cpp:3043
#2 0x00000000019f3f93 in ExecModifyTableWrap (node=0x7fdbf1836060) at execProcnode.cpp:785
#3 0x00000000019f43b5 in ExecProcNode (node=0x7fdbf1836060) at execProcnode.cpp:1038
#4 0x00000000019ed9d5 in ExecutePlan (estate=0x7fdbf0c74060, planstate=0x7fdbf1836060, operation=CMD_INSERT, sendTuples=false,
numberTuples=0, direction=ForwardScanDirection, dest=0x7fdbf13bb9c8, motJitContext=0x0) at execMain.cpp:2163
#5 0x00000000019ea25a in standard_ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0)
at execMain.cpp:608
#6 0x000000000181d6ef in explain_ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0)
at auto_explain.cpp:121
#7 0x00000000019e9dee in ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0) at execMain.cpp:486
#8 0x000000000194fed6 in ProcessQuery (plan=0x7fdbf0b7b2e0, sourceText=0x7fdbf13ba060 "insert into t1 values(1,200);", params=0x0,
isMOTTable=false, motJitContext=0x0, dest=0x7fdbf13bb9c8, completionTag=0x7fdbf3126020 "") at pquery.cpp:292
#9 0x0000000001953fa1 in PortalRunMulti (portal=0x7fdbf0c7a060, isTopLevel=true, dest=0x7fdbf13bb9c8, altdest=0x7fdbf13bb9c8,
completionTag=0x7fdbf3126020 "") at pquery.cpp:1889
#10 0x00000000019525e0 in PortalRun (portal=0x7fdbf0c7a060, count=9223372036854775807, isTopLevel=true, dest=0x7fdbf13bb9c8,
altdest=0x7fdbf13bb9c8, completionTag=0x7fdbf3126020 "") at pquery.cpp:1191
#11 0x000000000193ac65 in exec_simple_query (query_string=0x7fdbf13ba060 "insert into t1 values(1,200);",
messageType=QUERY_MESSAGE, msg=0x7fdbf3126210) at postgres.cpp:2720
以上分析了简单insert语句的两种执行流程,对于delete,update,select基本工作流程一致。
openGauss内核分析:SQL by pass & 经典执行器的更多相关文章
- openGauss内核:SQL解析过程分析
摘要:在传统数据库中SQL引擎一般指对用户输入的SQL语句进行解析.优化的软件模块.SQL的解析过程主要分为:词法.语法和语义分析. 本文分享自华为云社区< openGauss内核分析(三):S ...
- I2C(三) linux3.4(内核分析)
目录 I2C(三) linux3.4(内核分析) (一)总线流程 bus.probe match i2c_device_probe (二)client注册 方式(一)静态加载 方式(二)指定设备 方式 ...
- Linux内核分析4
周子轩原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 使用库函数API和C代码 ...
- 01MySQL内核分析-The Skeleton of the Server Code
摘要 这个官方文档一段对MySQL内核分析的一个向导.是对MySQL一条insert语句写入到MySQL数据库的分析. 但是,对于MySQL 5.7版本来说,基本上都是写入到innodb引擎.但也还是 ...
- linux内核分析作业8:理解进程调度时机跟踪分析进程调度与进程切换的过程
1. 实验目的 选择一个系统调用(13号系统调用time除外),系统调用列表,使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 分析汇编代码调用系统调用的工作过程,特别是参数的传递的方 ...
- Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序
1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...
- linux内核分析作业6:分析Linux内核创建一个新进程的过程
task_struct结构: struct task_struct { volatile long state;进程状态 void *stack; 堆栈 pid_t pid; 进程标识符 u ...
- linux内核分析作业5:分析system_call中断处理过程
1.增加 Menu 内核命令行 调试系统调用. 步骤:删除menu git clone (tab) make rootfs 这就是我们将 fork 函数写入 Menu 系统内核后的效果, ...
- linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作
一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...
- linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码
计算机如何工作 三个法宝:存储程序计算机.函数调用堆栈.中断机制. 堆栈 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 Esp 堆栈指针 (stack pointer) ...
随机推荐
- Android Kotlin 协程初探
1 它是什么(协程 和 Kotlin协程) 1.1 协程是什么 维基百科:协程,英文Coroutine [kəru'tin] (可入厅),是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被 ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-23-处理select下拉框-下篇
1.简介 上一篇中宏哥主要讲解和分享了一下,我们常见或者传统的select下拉框的操作,但是近几年又出现了了一种新的select下拉框,其和我们传统的select下拉框完全不一样,那么我们如何使用pl ...
- kubeadm 工具部署 kubernetes v1.16.2
环境准备 3个节点,以下基于 Centos 7.6 系统, 内核版本:3.10.0-957.12.2.e17.x86_64 HOST NODE CPU MEM 192.168.1.111 master ...
- 关于长链剖分的数组实现 | CF1009F Dominant Indices
请容许我不理解一下为什么这题题解几乎全都是指针实现/kk 其实长链剖分是可以直接用数组来写的. 考虑朴素 DP.设 \(f_{u,i}\) 表示以点 \(u\) 为根的子树中与点 \(u\) 距离为 ...
- umich cv-6-2 注意力机制
这节课中介绍了循环神经网络的第二部分,主要引入了注意力机制,介绍了注意力机制的应用以及如何理解,在此基础上建立了注意力层以及transformer架构 注意力机制 注意力机制 应用与理解 注意力层 t ...
- Ubuntu 编辑文件、安装、删除软件等常用命令(持续更新)
一.编辑文件 1. sudo vi 文件名,进入文件页面,如图: 2. 按 i 键或者 o 键,进入编辑,左下角出现---------INSERT---------信息 3. 输入完毕后,按ESC退出 ...
- [Python急救站课程]九九乘法表打印
打印九九乘法表 for i in range(1, 10): for j in range(1, i + 1): print("{}*{}={:2} ".format(j, i, ...
- 【scipy 基础】--线性代数
SciPy的linalg模块是SciPy库中的一个子模块,它提供了许多用于线性代数运算的函数和工具,如矩阵求逆.特征值.行列式.线性方程组求解等. 相比于NumPy的linalg模块,SciPy的li ...
- mySql中使用命令行建表基本操作
一:打开命令行启动mysql服务 注意事项:应该使用管理员身份打开命令行键入命令:net start mysql (鼠标右键使用管理员身份打开),否则会出现拒绝访问报错. 二:登陆数据库 登陆命令为& ...
- influxdb: unable to parse points 异常解决总结
转载请注明出处: influxdb 使用过程经常遇到:unable to parse points 的异常: unable to parse points 是 InfluxDB 抛出的异常,表示无 ...