前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Postgresql源码(126)TupleStore使用场景与原理分析

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

作者头像
mingjie
发布于 2024-04-18 01:08:50
发布于 2024-04-18 01:08:50
23600
代码可运行
举报
运行总次数:0
代码可运行

总结

  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函数

这个场景的惯用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1. 创建Dest Receiver
treceiver = CreateDestReceiver(DestTuplestore);
// 2 关键函数,配置TupleStore
SetTuplestoreDestReceiverParams(treceiver, tStore, .... ...)
// 3 treceiver传入执行器,调用执行器,结果进入tStore
// 4 清理DestReceiver
// 5 从tStore取出结果使用

用例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();

执行结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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中有了两条结果。

在这里插入图片描述
在这里插入图片描述

当前堆栈:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(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。

用例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();

结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的初始化。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void
PersistHoldablePortal(Portal portal)
{
	...

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	oldcxt = MemoryContextSwitchTo(portal->holdContext);

	portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);

	MemoryContextSwitchTo(oldcxt);

标记portal正在干活:READY → ACTIVE

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	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并开始执行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		queryDesc->dest = CreateDestReceiver(DestTuplestore);
		SetTuplestoreDestReceiverParams(queryDesc->dest,
										portal->holdStore,
										portal->holdContext,
										true,
										NULL,
										NULL);

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

在这里插入图片描述
在这里插入图片描述

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

继续分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		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}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	...
	...

	}
	...
	PG_END_TRY();

	MemoryContextSwitchTo(oldcxt);

portal用完了把状态改回来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	/* Mark portal not active */
	portal->status = PORTAL_READY;
	...
	...
}

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

SPI_cursor_fetch

commit后,在循环中会继续fetch游标,这里一个较大的区别时,不在执行portal拿结果了,从tuple store拿一行即可:

在这里插入图片描述
在这里插入图片描述

这里使用tuplestore_gettupleslot从tuple里面拿数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static uint64
RunFromStore(Portal portal, ScanDirection direction, uint64 count,
			 DestReceiver *dest)
{
	...
	...
	ok = tuplestore_gettupleslot(portal->holdStore, forward, false, slot);
	...
	...
	return current_tuple_count;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
请问有没有计费案例可以看?
请问有没有计费案例可以看?
11点赞举报
您好,您可以参考计费篇中的计费示例:https://cloud.tencent.com/developer/article/1876758
您好,您可以参考计费篇中的计费示例:https://cloud.tencent.com/developer/article/1876758
回复回复点赞举报
推荐阅读
实时音视频 TRTC 常见问题汇总---咨询问题篇
支持的平台包括 iOS、Android、Windows(C++)、Windows(C#)、Mac、Web、Electron、微信小程序、Flutter,更多详情请参见 平台支持。
腾讯视频云-Zachary
2019/11/01
13.3K0
实时音视频 TRTC 常见问题汇总---咨询问题篇
实时音视频 TRTC 常见问题汇总---集成接入篇
TRTC 的日志默认压缩加密,后缀为 .xlog。日志是否加密是可以通过 setLogCompressEnabled 来控制,生成的文件名里面含 C(compressed) 的就是加密压缩的,含 R(raw) 的就是明文的。
腾讯视频云-Zachary
2019/11/01
14.5K1
实时音视频 TRTC 常见问题汇总---集成接入篇
新知 | RT-ONE™&TRTC赋能实时音视频场景创新
今年腾讯云音视频发布了“三合一”的RT-ONE™网络。该网络整合了腾讯云实时通信网络(TRTC)、即时通信网络(IM)以及流媒体分发网络(CDN)三张网络,为业界最完整的音视频通信PaaS平台构建基座,面向教育、零售、泛娱乐等行业需求提供服务。本次新知系列的第一堂课,我们邀请到了腾讯云音视频的技术导师 —— 刘连响,为大家详解RT-ONE™并分享RT-ONE™&TRTC赋能实时音视频场景的一些创新。 接下来的5周,每周四晚上7:30,我们都会在腾讯云音视频视频号、开源中国、InfoQ、51CTO、云
腾讯云音视频
2021/11/22
2.2K6
实时音视频-腾讯云实时音视频(TRTC)
腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。
用户3570397
2019/08/06
10.7K0
你问我答 | 实时音视频TRTC(2021年5月-7月)
实时音视频TRTC 你问我答 第1季 本期共解答10个问题 Q1:移动端(Andriod/iOS)支持哪几种系统音量模式? 支持2种系统音量类型,即通话音量类型和媒体音量类型: 通话音量,手机专门为通话场景设计的音量类型,使用手机自带的回声抵消功能,音质相比媒体音量类型较差, 无法通过音量按键将音量调成零,但是支持蓝牙耳机上的麦克风。 媒体音量,手机专门为音乐场景设计的音量类型,音质相比于通话音量类型要好,通过通过音量按键可以将音量调成零。使用媒体音量类型时,如果要开启回声抵消(AEC
腾讯云音视频
2021/08/30
1.3K0
实时音视频 TRTC 常见问题汇总---小程序篇
如有更多需求,或希望深度合作,可以 提交工单 或致电4009100100联系我们。
腾讯视频云-Zachary
2021/09/21
5.8K0
实时音视频 TRTC 常见问题汇总---小程序篇
TRTC Android端开发接入学习之常见问题(十一)
V1和V2主要区别在于IM的SDK是否内嵌于TRTC中,V1线路是内嵌,而V2则可选,默认不打包IM的SDK包。V2在通话质量、线路规格、接入难度以及功能扩展上均比V1更有优势。
腾讯云-hongyang
2020/09/27
3.2K0
实时音视频开发学习10 - 小程序端一
小程序端双人通话、多人会议和语音聊天室三个功能。双人通话中实现1V1视频通话,在结合Web IM SDK可实现在线问诊或客服;语音聊天室能支持多人互动语音聊天,混音、混响等声音特效,适用于狼人杀、在线语音直播等社交场景;多人会议支持多路音视频通话、大小画面和屏幕分享等功能,适用于远程培训、在线教育等。
金林学音视频
2020/08/27
1.4K0
实时音视频开发学习10 - 小程序端一
使用TRTC Web SDK实现实时音视频通话
在使用 TRTC Web SDK 中,经常需要使用到两个对象,Client 客户端对象,Stream 流对象:
yuliang
2021/03/03
3.7K0
实时音视频开发学习1 - 音视频初体验
随着移动互联网速度越来越快的发展,实时音视频的发展也越来越收到重视。腾讯实时音视频(Tencent Real-Time Communication,TRTC)经过了10多年在网络音视频技术的发展与积累,并以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,使得开发者能快速实现低成本、低延时、高品质的音频互动解决方案。
金林学音视频
2020/08/20
4.1K0
实时音视频开发学习1 - 音视频初体验
实时音视频(TRTC)常见问题
一般而言,媒体音量指播放音乐、视频的声音、游戏声音等的音量,而通话音量指打电话的音量,视频通话的音量。
腾讯云-yyuanchen
2019/09/27
13.7K1
实时音视频 TRTC 常见问题汇总---WebRTC篇
TRTC Web SDK 对浏览器的详细支持度,您可以查看 TRTC Web SDK 对浏览器支持情况。
腾讯视频云-Zachary
2019/11/15
23.2K3
实时音视频 TRTC 常见问题汇总---WebRTC篇
实时音视频 TRTC 常见问题汇总---质量篇
如下代码所示,播放远端观众的画面渲染模式选择 TRTC_VIDEO_RENDER_MODE_FIT模式, 当渲染控件 View 的宽高比与视频宽高此不一致时,有黑边情况。
腾讯视频云-Zachary
2021/10/10
4.1K0
实时音视频 TRTC 常见问题汇总——计费案例
TRTC 是腾讯云基于 QQ 十多年来在音视频通话技术上积累,结合腾讯浏览服务 TBS WebRTC 能力与腾讯实时音视频 SDK ,为客户提供多平台互通高品质可定制化的 实时音视频互通服务 解决方案。
TRTC小百科
2021/09/27
1.5K0
iOS音视频接入 - TRTC常见问题
在 TRTC SDK 的示例代码中提供了一个叫做GenerateTestUserSig的开源模块,您只需要将其中的 SDKAPPID、EXPIRETIME 和 SECRETKEY 三个成员变量修改成您自己的配置,就可以调用genTestUserSig()函数获取计算好的 UserSig。
小明同学接音视频
2020/10/21
3K0
iOS音视频接入 - TRTC常见问题
实时音视频开发学习14 - 常见问题
V1和V2主要区别在于IM的SDK是否内嵌于TRTC中,V1线路是内嵌,而V2则可选,默认不打包IM的SDK包。V2在通话质量、线路规格、接入难度以及功能扩展上均比V1更有优势。
金林学音视频
2020/08/30
2.8K0
实时音视频开发学习14 - 常见问题
入门腾讯实时音视频(TRTC)从这里开始
腾讯实时音视频(Tencent Real-Time Communication,TRTC)将腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。产品详情 >>
smiling
2020/03/30
8.9K2
入门腾讯实时音视频(TRTC)从这里开始
实时音视频V2版本,如何进行纯音频旁路直播
实时音视频TRTCSDK适用的业务场景是视频会议、坐席通话、在线教育等,也可以实现类似微信的语音通话、语音会议功能,
腾讯云-chaoli
2019/03/13
3.5K0
实时音视频V2版本,如何进行纯音频旁路直播
实时音视频技术的演进与应用
本次分享我们邀请到了来自腾讯云实时音视频TRTC后台的研发负责人薛笛,他向我们分享了腾讯云TRTC在架构升级和产品实践中的经验。仔细讲解了混音引擎最初的制造源、在整个优化过程中发现的问题以及解决方法,为后来做腾讯会议和云呼叫中心打下了一个良好的基础。
LiveVideoStack
2021/05/08
1.7K0
实时音视频技术的演进与应用
iOS 音视频接入 - 初识TRTC
在上一篇文章中我们对音视频有了最基础的认识,下面就来了解下第三方提供的功能强大的实时音视频SDK-TRTC。
小明同学接音视频
2020/10/09
3K0
iOS 音视频接入 - 初识TRTC
推荐阅读
相关推荐
实时音视频 TRTC 常见问题汇总---咨询问题篇
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • TupleStore使用场景一:RETURNS SETOF函数
    • 1 exec_stmt_return_query
    • 2 exec_stmt_return
    • 3 plpgsql_exec_function
  • TupleStore使用场景二:游标持久化(TupleStore作为Dest Receiver)
    • 1 HoldPinnedPortals
    • 2 HoldPortal
    • 3 HoldPortal→PortalCreateHoldStore
    • 3 HoldPortal→PersistHoldablePortal
    • SPI_cursor_fetch
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档