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

news2024/11/26 12:22:37

相关
《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 $$
BEGIN
    RETURN 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    | t
  3 | 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 $$
DECLARE
	rec1 record;
BEGIN
for rec1 in select t.a from generate_series(1,10) t(a)
loop
  raise 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.coloradmin.cn/o/1604365.html

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

相关文章

回文链表leecode

回文链表 偶数情况奇数情况 回文链表leecode 偶数情况 public boolean isPalindrome(ListNode head) {if (head null) {return true;}ListNode fast head;ListNode slow head;while (fast ! null && fast.next ! null) {fast fast.next.next;slow slow.next;}//反…

Day43:LeedCode 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…

Excel文件解析(Java)

一、概述 在应用程序的开发过程中&#xff0c;经常需要使用 Excel文件来进行数据的导入或导出。所以&#xff0c;在通过Java语言实现此类需求的时候&#xff0c;往往会面临着Excel文件的解析(导入&#xff09;或生成&#xff08;导出)。 在Java技术生态圈中&#xff0c…

Java实现RSA加密示例代码

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 RSA加密是一种非对…

如何配置Postgres的自动扩展功能以应对数据增长

文章目录 解决方案1. 表空间管理2. 分区表3. 自动扩展配置4. 监控和告警5. 使用外部工具和服务 示例代码示例1&#xff1a;创建表空间示例2&#xff1a;创建分区表示例3&#xff1a;调整配置参数示例4&#xff1a;使用监控和告警工具 总结 在PostgreSQL中&#xff0c;随着数据的…

QT-编译报库错误(LF/CRLF)

QT-安装后环境问题记录 版本和环境问题 版本和环境 QT5.15.2 Windows10 QT Creator 问题 在QT夸端开发的项目中 &#xff0c;使用QTCreator打开项目pro文件&#xff0c;编译报出很多系统库 及本地文件中的一些问题&#xff0c;具体如图&#xff1a; 后续&#xff0c;我以为…

宠物店小程序如何搭建制作?宠物店小程序核心功能有哪些?

随着宠物经济的兴起&#xff0c;宠物店的线上服务需求日益增长。微信小程序作为一种便捷的线上服务平台&#xff0c;为宠物店提供了一个与爱宠人士建立联系的新渠道。面对市场上众多的小程序开发选项&#xff0c;宠物店应该如何选择或制作一款适合自己的小程序呢&#xff1f;本…

Java高阶私房菜:快速学会异步编程CompletableFuture

为了使主程代码不受阻塞之苦&#xff0c;一般使用异步编程&#xff0c;而异步编程架构在JDK1.5便已有了雏形&#xff0c;主要通过Future和Callable实现&#xff0c;但其操作方法十分繁琐&#xff0c;想要异步获取结果,通常要以轮询的方式去获取结果&#xff0c;具体如下&#x…

登录github突然出现Two-factor authentication怎么办?

起因 今天想登录github下载项目&#xff0c;输入用户名密码认证之后竟然弹出来了Two-factor authentication&#xff0c;像下图&#xff0c;我只记得之前好像开启过这玩意&#xff0c;但是不知道哪里找这个code了。 解决 在你的iphone里面搜索密码。然后找到github网页&…

程序员能做什么副业?增收致富就靠它们了

作为一个程序员&#xff0c;大家都习惯了朝九晚六&#xff0c;时不时加班的生活。虽然工资听起来比其他行业高一些&#xff0c;但是&#xff0c;都是靠命拼出来的。如何摆脱这种枯燥乏味且前途未卜的生活&#xff0c;应该是很多程序员都在考虑的事情。 而作为一个做了十几年的…

【微服务-Ribbon】什么是负载均衡?微服务中负载均衡有哪些策略呢?

前面几篇文章&#xff0c;我们了解了一下Nacos的单机部署、集群部署以及微服务接入Nacos的步骤。从本篇开始&#xff0c;我们来看一下微服务第二个通用组件-负载均衡&#xff08;Ribbon&#xff09;。 1、Ribbon负载均衡器 负载均衡顾名思义&#xff0c;是指通过软件或者硬件…

javaWeb智能医疗管理系统

简介 在当今快节奏的生活中&#xff0c;智能医疗系统的崛起为医疗行业带来了一场革命性的变革。基于JavaWeb技术开发的智能医疗管理系统&#xff0c;不仅为医疗机构提供了高效、精准的管理工具&#xff0c;也为患者提供了更便捷、更个性化的医疗服务。本文将介绍一个基于SSM&a…

LInux shell编程之基础语法

目录 1、shell概述 1.1、查看Linux 提供的 Shell 解析器 1.2、bash 和 sh 的关系 1.3、查看系统&#xff08;centos&#xff09;默认使用的解释器 2、脚本的基础使用 2.1、脚本的格式 2.2、脚本的执行方式 方式一&#xff1a;采用 bash / sh 脚本的相对路径或绝对路径…

【LeetCode刷题记录】160. 相交链表

160 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函…

沂水六景记 ——齐源堂主王志强撰文

沂水六景记 ——齐源堂主王志强 明清时期&#xff0c;非常流行“八景”。所谓“八景”&#xff0c;就是在本地众多的风景名胜之中&#xff0c;选定八个最有代表性的美景&#xff0c;分别取上悦耳动听的名字&#xff0c;然后分别题诗。这种给风景选美的做法&#xff0c;是文人…

ASP.NET基于TCP协议的简单即时通信软件的设计与实现

摘 要 即时通信(Instant Message)&#xff0c;由于其具有实时性、跨平台性、成本低、效率高等优点而受到广泛的使用。设计并实现一个能够处理多用户进行实时、安全的即时通信系统具有较强的现实意义。即时通信的底层通信是通过SOCKET套接字接口实现的。当前的主流UNIX系统和微…

2W,3KVDC隔离 定电压输入,稳压单、双路输出DC-DC模块电源——TPI-2W 系列

TPI-2W系列产品是专门针对PCB上需要与输入电源隔离的电源应用场合而设计的。该产品适用于&#xff1a;1&#xff09;输入电源的电压变化≤5%&#xff1b;2&#xff09;输入输出之间要求隔离电压≥3000VDC&#xff1b;3&#xff09;对输出电压稳定和输出纹波噪声要求高.

linux 安装openjdk-1.8

安装命令 yum install java-1.8.0-openjdk-1.8.0.262.b10-1.el7.x86_64查看安装路径 find / -name java 默认的安装路径 /usr/lib/jvm 查看到jre 以及java-1.8.0-openjdk-1.8.0.262.b10-1.el7.x86_64 配置环境变量 vim /etc/profile 添加的内容 export JAVA_HOME/usr/li…

网络工程师----第三天

HDLC帧格式及控制手段&#xff1a; ipv4到ipv6的过渡技术&#xff1a; 1、双栈技术&#xff1a;主机或路由器同时装有IPV4 和 IPV6两个协议栈&#xff0c;因此&#xff0c;主机既能和IPV4通信&#xff0c;也能和IPv6网络通信。IPv6和IPv4是功能相近的网络层协议&#xff0c;两者…

爱普生计时设备AUTOMOTIVE RA8900CE DTCXO RTC

主要特点出场已校准带有DTCXO的RTC&#xff0c;并且内部集成晶体单元高精度: 3.4 ppm 40 to 85 C(9 s/月.)时钟输出:1 Hz.1024 Hz.32.768 kHzI 2 C Interface: Fast mode (400 kHz)The l2C-Bus is a trademark ofNXP Semiconductors供电电压: 2.5-5.5 V(main),1.6-5.5 V(备份电…