Postgresql源码(126)TupleStore使用场景与原理分析

相关
《Postgresql源码(125)游标恢复执行的原理分析》
《Postgresql游标使用介绍(cursor)》

总结

  1. 开源PG中使用tuple store来缓存tuple集,默认使用work_mem空间存放,超过可以落盘。
  2. 在PL的returns setof场景 和 loop内commit的场景 会使用tuple store暂存元组。
  3. tuple store的使用方法
    1. 配置dest reveiver为tuple store:CreateDestReceiver / SetTuplestoreDestReceiverParams
    2. 执行:ExecutorRun
    3. 使用tuple store:
      • PL返回的tuplestore拿tuple:FunctionNext → tuplestore_gettupleslot
      • 游标hold后从tuplestore拿tuple:RunFromStore → tuplestore_gettupleslot

TupleStore使用场景一:RETURNS SETOF函数

这个场景的惯用法如下:

// 1. 创建Dest Receiver
treceiver = CreateDestReceiver(DestTuplestore);
// 2 关键函数,配置TupleStore
SetTuplestoreDestReceiverParams(treceiver, tStore, .... ...)
// 3 treceiver传入执行器,调用执行器,结果进入tStore
// 4 清理DestReceiver
// 5 从tStore取出结果使用

用例

create table users(id int, name text, active bool);
insert into users values(1, 'a', true);
insert into users values(2, 'b', false);
insert into users values(3, 'c', true);CREATE OR REPLACE FUNCTION get_active_users() RETURNS SETOF users AS $$
BEGINRETURN QUERY SELECT * FROM users WHERE active = true;
END;
$$ LANGUAGE plpgsql;select * from get_active_users();

执行结果

postgres=# select * from get_active_users();id | name | active
----+------+--------1 | a    | t3 | c    | t
(2 rows)

1 exec_stmt_return_query

执行时,会走到exec_stmt_return_query中获取执行结果:
在这里插入图片描述

调用SPI_execute_plan_extended执行后,可以看到tstore中有了两条结果。
在这里插入图片描述
当前堆栈:

(gdb) bt
#0  exec_stmt_return_query (estate=0x7ffc569f6950, stmt=0x17fe708) at pl_exec.c:3655
#1  0x00007fbaa37bc602 in exec_stmts (estate=0x7ffc569f6950, stmts=0x17fec18) at pl_exec.c:2079
#2  0x00007fbaa37bc229 in exec_stmt_block (estate=0x7ffc569f6950, block=0x17fec68) at pl_exec.c:1942
#3  0x00007fbaa37bba2b in exec_toplevel_block (estate=0x7ffc569f6950, block=0x17fec68) at pl_exec.c:1633
#4  0x00007fbaa37b99be in plpgsql_exec_function (func=0x16d72f0, fcinfo=0x17fb7d0, simple_eval_estate=0x0, simple_eval_resowner=0x0, procedure_resowner=0x0, atomic=true) at pl_exec.c:622
#5  0x00007fbaa37d4151 in plpgsql_call_handler (fcinfo=0x17fb7d0) at pl_handler.c:277
#6  0x000000000075c75d in ExecMakeTableFunctionResult (setexpr=0x1763e68, econtext=0x1763d38, argContext=0x17fb6d0, expectedDesc=0x1764138, randomAccess=false) at execSRF.c:234
#7  0x0000000000777ca7 in FunctionNext (node=0x1763b28) at nodeFunctionscan.c:94
#8  0x000000000075df9f in ExecScanFetch (node=0x1763b28, accessMtd=0x777bf5 <FunctionNext>, recheckMtd=0x777fff <FunctionRecheck>) at execScan.c:131
#9  0x000000000075e014 in ExecScan (node=0x1763b28, accessMtd=0x777bf5 <FunctionNext>, recheckMtd=0x777fff <FunctionRecheck>) at execScan.c:180
#10 0x0000000000778049 in ExecFunctionScan (pstate=0x1763b28) at nodeFunctionscan.c:269
#11 0x0000000000759f16 in ExecProcNodeFirst (node=0x1763b28) at execProcnode.c:464
#12 0x000000000074dccf in ExecProcNode (node=0x1763b28) at ../../../src/include/executor/executor.h:274
#13 0x00000000007507cc in ExecutePlan (estate=0x1763900, planstate=0x1763b28, use_parallel_mode=false, operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x17df418, execute_once=true) at execMain.c:1646
#14 0x000000000074e367 in standard_ExecutorRun (queryDesc=0x17dfb90, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:363
#15 0x000000000074e17b in ExecutorRun (queryDesc=0x17dfb90, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:304
#16 0x00000000009e6e62 in PortalRunSelect (portal=0x1787ba0, forward=true, count=0, dest=0x17df418) at pquery.c:924
#17 0x00000000009e6b20 in PortalRun (portal=0x1787ba0, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x17df418, altdest=0x17df418, qc=0x7ffc569f7180) at pquery.c:768
#18 0x00000000009e06a2 in exec_simple_query (query_string=0x16dd080 "select * from get_active_users();") at postgres.c:1274
#19 0x00000000009e4cfb in PostgresMain (dbname=0x16d7910 "postgres", username=0x1715bf8 "mingjie") at postgres.c:4680
#20 0x00000000009dd07f in BackendMain (startup_data=0x7ffc569f748c "", startup_data_len=4) at backend_startup.c:101
#21 0x000000000090be72 in postmaster_child_launch (child_type=B_BACKEND, startup_data=0x7ffc569f748c "", startup_data_len=4, client_sock=0x7ffc569f74b0) at launch_backend.c:265
#22 0x0000000000911702 in BackendStartup (client_sock=0x7ffc569f74b0) at postmaster.c:3593
#23 0x000000000090ebf3 in ServerLoop () at postmaster.c:1674
#24 0x000000000090e5ad in PostmasterMain (argc=1, argv=0x16d58c0) at postmaster.c:1372
#25 0x00000000007d20ac in main (argc=1, argv=0x16d58c0) at main.c:197

2 exec_stmt_return

继续执行到exec_stmt_return,返回值是SET不需要干活。
在这里插入图片描述

3 plpgsql_exec_function

在plpgsql_exec_function最后,处理刚才保存在tstore里面的元组:
在这里插入图片描述
注意这里的estate->rsi指向的是fcinfo->resultinfo,在这里配置的:
在这里插入图片描述
在这里插入图片描述
这样结果数据就可以通过function调用框架返回给SQL层了。

TupleStore使用场景二:游标持久化(TupleStore作为Dest Receiver)

两条路径会使用到游标持久化的功能:

  1. 第一种是创SQL层游标时,使用with hold语法,游标可以跨多个事务存在。
  2. 第二种是循环体内执行commit,这时候循环游标正常会跟着最近一层事务被删掉,但循环还需要继续执行,所以需要hold给循环游标续命。
    在这里插入图片描述

后面分析下第二种场景,循环游标提交hold。

用例


drop procedure tproc1;
CREATE OR REPLACE PROCEDURE tproc1() AS $$
DECLARErec1 record;
BEGIN
for rec1 in select t.a from generate_series(1,10) t(a)
loopraise notice '%', rec1.a;commit;raise notice '%', rec1.a;end loop;
END;
$$ LANGUAGE plpgsql;call tproc1();

结果

postgres=# call tproc1();
NOTICE:  1
NOTICE:  1
NOTICE:  2
NOTICE:  2
NOTICE:  3
NOTICE:  3
NOTICE:  4
NOTICE:  4
NOTICE:  5
NOTICE:  5
NOTICE:  6
NOTICE:  6
NOTICE:  7
NOTICE:  7
NOTICE:  8
NOTICE:  8
NOTICE:  9
NOTICE:  9
NOTICE:  10
NOTICE:  10
CALL

1 HoldPinnedPortals

在这里插入图片描述

2 HoldPortal

在这里插入图片描述

3 HoldPortal→PortalCreateHoldStore

  1. 创建Context,切换过去。
  2. 在新上下文中,执行tuplestore的初始化。
void
PortalCreateHoldStore(Portal portal)
{MemoryContext oldcxt;portal->holdContext =AllocSetContextCreate(TopPortalContext,"PortalHoldContext",ALLOCSET_DEFAULT_SIZES);oldcxt = MemoryContextSwitchTo(portal->holdContext);portal->holdStore =tuplestore_begin_heap(portal->cursorOptions & CURSOR_OPT_SCROLL,true, work_mem);MemoryContextSwitchTo(oldcxt);
}

3 HoldPortal→PersistHoldablePortal

void
PersistHoldablePortal(Portal portal)
{...

tuplestore需要再自己的上下文中保存一份desc。

	oldcxt = MemoryContextSwitchTo(portal->holdContext);portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);MemoryContextSwitchTo(oldcxt);

标记portal正在干活:READY → ACTIVE

	MarkPortalActive(portal);......PG_TRY();{ScanDirection direction = ForwardScanDirection;ActivePortal = portal;if (portal->resowner)CurrentResourceOwner = portal->resowner;PortalContext = portal->portalContext;MemoryContextSwitchTo(PortalContext);PushActiveSnapshot(queryDesc->snapshot);...

创建tuple store的dest receiver并开始执行:

		queryDesc->dest = CreateDestReceiver(DestTuplestore);SetTuplestoreDestReceiverParams(queryDesc->dest,portal->holdStore,portal->holdContext,true,NULL,NULL);

注意这里ExecutorRun的第三个参数是0,表示拿完为止。和游标fetch是有区别的,fetch一次这里会传入1,只拿一条。
在这里插入图片描述

参考这篇:《Postgresql源码(125)游标恢复执行的原理分析》

继续分析:

		ExecutorRun(queryDesc, direction, 0, false);queryDesc->dest->rDestroy(queryDesc->dest);queryDesc->dest = NULL;portal->queryDesc = NULL;	/* prevent double shutdown */ExecutorFinish(queryDesc);ExecutorEnd(queryDesc);FreeQueryDesc(queryDesc);MemoryContextSwitchTo(portal->holdContext);

执行后tuples=9,之前已经查出来一条,现在把剩下的9条都拿到了。
(gdb) p *portal->holdStore $6 = {status = TSS_INMEM, eflags = 4, backward = false, interXact = true, truncated = false, availMem = 8371768, allowedMem = 8388608, tuples = 9, myfile = 0x0, context = 0x1855da0, resowner = 0x1717530, copytup = 0xc0051f <copytup_heap>, writetup = 0xc0056a <writetup_heap>, readtup = 0xc00635 <readtup_heap>, memtuples = 0x1821e68, memtupdeleted = 0, memtupcount = 9, memtupsize = 2048, growmemtuples = true, readptrs = 0x1855fb0, activeptr = 0, readptrcount = 1, readptrsize = 8, writepos_file = 0, writepos_offset = 0}

	......}...PG_END_TRY();MemoryContextSwitchTo(oldcxt);

portal用完了把状态改回来:

	/* Mark portal not active */portal->status = PORTAL_READY;......
}

至此,游标内容已经全部进入tuple store。

SPI_cursor_fetch

commit后,在循环中会继续fetch游标,这里一个较大的区别时,不在执行portal拿结果了,从tuple store拿一行即可:
在这里插入图片描述
这里使用tuplestore_gettupleslot从tuple里面拿数据:

static uint64
RunFromStore(Portal portal, ScanDirection direction, uint64 count,DestReceiver *dest)
{......ok = tuplestore_gettupleslot(portal->holdStore, forward, false, slot);......return current_tuple_count;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/824214.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pascal VOC(VOC 2012、VOC 2007) 数据集的简介

一、数据集介绍 PascalVOC(2005~2012)数据集是PASCAL VOC挑战官方使用的数据集。该数据集包含20类的物体。每张图片都有标注&#xff0c;标注的物体包括人、动物&#xff08;如猫、狗、岛等&#xff09;、交通工具&#xff08;如车、船飞机等&#xff09;、家具&#xff08;如椅…

Redux极客园项目初始化搭建

基本结构搭建 实现步骤 在 Login/index.js 中创建登录页面基本结构在 Login 目录中创建 index.scss 文件&#xff0c;指定组件样式将 logo.png 和 login.png 拷贝到 assets 目录中 代码实现 pages/Login/index.js import ./index.scss import { Card, Form, Input, Button }…

【LLM】认识LLM

文章目录 1.LLM1.1 LLM简介1.2 LLM发展1.3 市面常见的LLM1.4 LLM涌现的能力 2.RAG2.1 RAG简介2.2 RAG 的工作流程2.3 RAG 和 Finetune 对比2.4 RAG的使用场景分析 3. LangChain3.1 LangChain简介3.2 LangChain的核心组件3.3 LangChain 入门 4.开发 RAG 应用的整体流程5. 环境配…

GPT状态和原理 - 解密OpenAI模型训练

目录 1 如何训练 GPT 助手 1.1 第一阶段 Pretraining 预训练 1.2 第二阶段&#xff1a;Supervised Finetuning有监督微调 1.3 第三阶段 Reward Modeling 奖励建模 1.4 第四阶段 Reinforcement Learning 强化学习 1.5 总结 2 第二部分&#xff1a;如何有效的应用在您的应…

原生实现ajax

1 什么是ajax AJAX Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09;。 AJAX 不是新的编程语言&#xff0c;而是一种使用现有标准的新方法。 AJAX 最大的优点是在不重新加载整个页面的情况下&#xff0c;可以与服务器交换数据并更新部分网…

unity学习(85)——同步节奏(tcp架构确实有问题)

挂的时间长了&#xff0c;就出现其他下线本地不destroy的情况了&#xff0c;而且此时再登录&#xff0c;新渲染中已经没有已经下线的玩家 unity这边就没有收到126&#xff01;&#xff01;&#xff01;125的问题是多种多样的&#xff01;&#xff01;&#xff01; 化简服务器w…

设计循环队列(队列oj)

1.设计循环队列 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。…

【笔记】vscode debug进入site-packages包源码

选择左侧栏第三个图标&#xff0c;点击创建 launch.json 文件 选择 Python Debugger 选择Python文件 这里可以看到launch.json 文件 在configurations中添加键值对 "justMyCode": false在文件中打上断点&#xff0c;点击"三角符"号开始调试 按F11或者红框…

reportlab 生成pdf文件 (python)

1 安装 pip install reportlab2 应用场景 通过网页动态生成PDF文档大量的报告和数据发布用XML一步生成PDF 官网案例 3 PLATYPUS Platypus是“Page Layout and Typography Using Scripts”&#xff0c;是使用脚本的页面布局和印刷术的缩写&#xff0c;这是一个高层次页面布局…

【面试题】MySQL 事务的四大特性说一下?

事务是一个或多个 SQL 语句组成的一个执行单元&#xff0c;这些 SQL 语句要么全部执行成功&#xff0c;要么全部不执行&#xff0c;不会出现部分执行的情况。事务是数据库管理系统执行过程中的一个逻辑单位&#xff0c;由一个有限的数据库操作序列构成。 事务的主要作用是保证数…

记一次kafkakerberos认证问题

1&#xff0c;报错信息 排查思路&#xff1a;检查kerberos配置文件 kerberos.kafka.principalkafka/huawe_baseSECURITY.COM kerberos.kafka.keytabPath/etc/huawe_base.keytab kerberos.kafka.krb5ConfPath/etc/krb5.conf但是查看kafka_client_jass.conf文件&#xff0c;发现…

网络基础-TCP/IP和OSI协议模型

一、OSI和TCP/IP模型 二、OSI七层模型 三、TCP/IP模型 参考&#xff1a;https://www.cnblogs.com/f-ck-need-u/p/7623252.html

关联规则挖掘(二)

目录 三、FP-增长算法&#xff08;一&#xff09;算法的背景&#xff08;二&#xff09;构造FP-树&#xff08;三&#xff09;生成频繁项集 四、关联规则的评价&#xff08;一&#xff09;支持度和置信度的不足&#xff08;二&#xff09;相关性分析 三、FP-增长算法 &#xf…

.NET 发布,部署和运行应用程序

.NET应用发布 发布.Net应用有很多种方式&#xff0c;下面列举三种发布方式&#xff1a; 单文件发布跨平台发布Docker发布 单文件发布 右键工程&#xff0c;选择“发布”&#xff0c;部署模式选择“独立”&#xff0c;目标运行时选择自己想要部署到的系统&#xff0c;我这里用…

问题:react函数中的state是上一次的值

场景 有一个聊天输入框组件&#xff0c;输入框上面有表情包组件。 通过redux创建了store&#xff0c;存储一个message的状态&#xff0c;用于表情包和输入框共享状态。 输入框通过设置value和onClick做了一个简单双向绑定&#xff0c;其中value的值为store里的message。 impor…

【Java框架】Spring框架(三)——Spring整合Mybatis及Spring声明式事务

目录 回顾Mybatis和新对象思路整理 Spring和MyBatis的整合步骤1. 创建Web工程&#xff0c;导入Spring和MyBatis的相关依赖2. 建立开发目录结构&#xff0c;创建实体类3. 创建数据访问接口和SQL映射语句文件4. 使用Spring配置文件配置数据源4.1 database.properties4.2spring配置…

全国产化无风扇嵌入式车载电脑在救护车远端诊断的行业应用

救护车远端诊断的行业应用 背景介绍 更加快速的为急症病人在第一时间开始进行诊断和治疗,是提高病人救助成功率的关键。因此&#xff0c;先进的救护系统正在思考&#xff0c;如何在病人进入救护车之后&#xff0c;立刻能够将救护车中各种检查仪器的信息快速的传回医院&#xf…

aws云靶场和一些杂记

aws靶场 在AWS靶场中&#xff0c;存在三个安全问题&#xff1a;1) 一个S3存储桶政策配置错误&#xff0c;允许公共访问&#xff0c;通过访问特定域名可获取flag。2) SQS消息队列的政策没有限制角色&#xff0c;允许发送和接收消息&#xff0c;通过aws sqs命令行工具的receive-…

JS-42-Node.js01-Node.js介绍

一、浏览器大战 众所周知&#xff0c;在Netscape设计出JavaScript后的短短几个月&#xff0c;JavaScript事实上已经是前端开发的唯一标准。 后来&#xff0c;微软通过IE击败了Netscape后一统桌面&#xff0c;结果几年时间&#xff0c;浏览器毫无进步。&#xff08;2001年推出…

C++面向对象程序设计-北京大学-郭炜【课程笔记(七)】

C面向对象程序设计-北京大学-郭炜【课程笔记&#xff08;七&#xff09;】 1、类型转换运算符2、自增、自减运算符的重载3、继承和派生的基本概念3.1、基本概念3.2、派生类对象的内存空间 4、继承关系和复合关系4.1、继承关系的使用4.2、复合关系的使用 5、派生类覆盖基类成员6…