前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(72)ResourceOwner资源管理器

Postgresql源码(72)ResourceOwner资源管理器

作者头像
mingjie
发布2022-09-26 21:50:15
7740
发布2022-09-26 21:50:15
举报

ResourceOwner设计比较简单,这里做下总结,方便后面查询。

速查:

  1. ResourceOwner是多层结构,每次批量释放时,只释放自己同层的资源,不释放父节点资源(同层指针child->nextchild)。
  2. ResourceOwner批量释放提供三段释放的方法,每次调用只释放某些特定资源。
  3. 子事务会多层的ResourceOwner结构,注意不是同层的,每次savepoint都会多一层ResourceOwner。

1 顶层ResourceOwner生命周期

单SQL执行为例:

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

注意申请的两个ResourceOwner会有层次关系,最外层套的是TopTransaction,内层是Portal。

代码语言:javascript
复制
        CurrentResourceOwner
                   |
                   |
                   v
{parent = 0x1996c58, firstchild = 0x0, nextchild = 0x0, name = 0xd47445 "Portal", ... }
    |              ^
    |              |
    v              |
{parent = 0x0, firstchild = 0x198a520, nextchild = 0x0, name = 0xbafcac "TopTransaction", ... }

2 如何记录资源?

2.1 数据结构

  1. 每种资源对应一个数组:ResourceArray。
  2. 数组设计成了通用数据存储空间,几个变量的作用:itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity.
  3. 数组达到一定大小后(最大64),后转换为开放地址哈希表。
代码语言:javascript
复制
typedef struct ResourceArray
{
	Datum	   *itemsarr;		/* buffer for storing values */
	Datum		invalidval;		/* value that is considered invalid */
	uint32		capacity;		/* allocated length of itemsarr[] */
	uint32		nitems;			/* how many items are stored in items array */
	uint32		maxitems;		/* current limit on nitems before enlarging */
	uint32		lastidx;		/* index of last item returned by GetAny */
} ResourceArray;

typedef struct ResourceOwnerData
{
ResourceOwner parent; /* NULL if no parent (toplevel owner) */
ResourceOwner firstchild; /* head of linked list of children */
ResourceOwner nextchild; /* next child of same parent */
const char *name; /* name (just for debugging) */

/* We have built-in support for remembering: */
ResourceArray bufferarr; /* owned buffers */
ResourceArray catrefarr; /* catcache references */
ResourceArray catlistrefarr; /* catcache-list pins */
ResourceArray relrefarr; /* relcache references */
ResourceArray planrefarr; /* plancache references */
ResourceArray tupdescarr; /* tupdesc references */
ResourceArray snapshotarr; /* snapshot references */
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
ResourceArray cryptohasharr; /* cryptohash contexts */
ResourceArray hmacarr; /* HMAC contexts */

/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
} ResourceOwnerData;

2.2 算法

2.2.1 记录pin buffer
代码语言:javascript
复制
typedef struct ResourceArray
{
	Datum	   *itemsarr;		/* buffer for storing values */
	Datum		invalidval;		/* value that is considered invalid */
	uint32		capacity;		/* allocated length of itemsarr[] */
	uint32		nitems;			// 指向当前数组使用位置
	uint32		maxitems;		/* current limit on nitems before enlarging */
	uint32		lastidx;		/* index of last item returned by GetAny */
} ResourceArray;

以记录Pin了哪些buffer为例:

  • 第一步:初始化记录数组:ResourceOwnerEnlargeBuffers(ReadBuffer_common调用)
    • 1 capacity扩展为16。
    • 2 itemsarr记录申请的16个指针空间in TopMemoryContext(注意类型是Datum,可以存指针【引用】,也可以直接存值【值】)
    • 3 itemsarr记录的16个指针初始化:指向该类型提供的“无效值”:invalidval。
  • 第二步:记录buffer:ResourceOwnerRememberBuffer(PinBuffer调用)
    • 1 入参:(CurrentResourceOwner, b),b为Buffer类型:Buffer ID整形。
    • 2 【数组形态】保存ID不够64个
      • nitems按顺序拿到使用位置
      • 用nitems位置记录ID:resarr->itemsarr[idx] = value
    • 3 【哈希形态】保存ID超过64个,走开放hash下面具体分析。

UnPin操作:

  • 第一步:删除记录:ResourceOwnerForgetBuffer

开放hash

  1. hash到当前空间能存的下的一个位置上。
  2. 如果位置为空:使用。
  3. 如果位置有值:+1向后搜索使用。
代码语言:javascript
复制
static void
ResourceArrayAdd(ResourceArray *resarr, Datum value)
{
	uint32		idx;
	// resarr->capacity <= 64
	if (RESARRAY_IS_ARRAY(resarr))
	{
		idx = resarr->nitems;
	}
	else
	{
	    // 到这resarr->capacity最少为128
		uint32		mask = resarr->capacity - 1;
		
		// hash value 映射到 0 - 127的位置上
		idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
		for (;;)
		{
			if (resarr->itemsarr[idx] == resarr->invalidval)
				break;
			idx = (idx + 1) & mask;
		}
	}
	resarr->lastidx = idx;
	resarr->itemsarr[idx] = value;
	resarr->nitems++;
}
2.2.2 记录lock

锁比较特殊,没用ResourceArray数组,直接使用ResourceOwnerData中的locks数组。

代码语言:javascript
复制
typedef struct ResourceOwnerData
{
...
    int nlocks; /* number of owned locks */
    LOCALLOCK *locks[15]; /* list of owned locks */
} ResourceOwnerData;

记录常规锁:

ResourceOwnerRememberLock:记录Local常规锁,只记录15把锁,多了直接放弃。

删除记录常规锁:

ResourceOwnerForgetLock:删除记录Local常规锁,只记录15把锁,多了直接放弃。

3 如何统一释放资源?

递归,遍历当前owner同一层的所有owner,按child->nextchild来找;注意,不会释放parent的资源!

代码语言:javascript
复制
ResourceOwnerRelease
  ResourceOwnerReleaseInternal
    	for (child = owner->firstchild; child != NULL; child = child->nextchild)
		  ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
        ..
        ..
        【干活】
        ...
        ...

【干活】:

释放函数提供三种模式,释放不同资源:

代码语言:javascript
复制
typedef enum
{
	RESOURCE_RELEASE_BEFORE_LOCKS,
	RESOURCE_RELEASE_LOCKS,
	RESOURCE_RELEASE_AFTER_LOCKS
} ResourceReleasePhase;

调用三次按顺序释放不同资源:

代码语言:javascript
复制
PortalDrop
		...
		ResourceOwnerRelease(portal->resowner,
							 RESOURCE_RELEASE_BEFORE_LOCKS,
							 isCommit, false);
		ResourceOwnerRelease(portal->resowner,
							 RESOURCE_RELEASE_LOCKS,
							 isCommit, false);
		ResourceOwnerRelease(portal->resowner,
							 RESOURCE_RELEASE_AFTER_LOCKS,
							 isCommit, false);

ResourceOwnerRelease具体工作:

每种资源调用自己的释放函数,都类似下面释放过程

  • 1 拿到当前数组记录的所有资源项
  • 2 调用当前资源回收函数进行回收
代码语言:javascript
复制
...
		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
		{
			Buffer		res = DatumGetBuffer(foundres);
			ReleaseBuffer(res);
		}
...

4 子事务ResourceOwner层次关系

代码语言:javascript
复制
drop table table1;
create table table1(i int);

BEGIN;
    INSERT INTO table1 VALUES (1);
    -- s1
    SAVEPOINT my_savepoint1;
    INSERT INTO table1 VALUES (2);
    
    -- s2
    SAVEPOINT my_savepoint2;
    INSERT INTO table1 VALUES (3);

...
...

两个检查点:两层子事务:

代码语言:javascript
复制
顶层:0x1996c58
 p *CurrentResourceOwner->parent->parent
 {parent = 0x0, firstchild = 0x198a520中层, nextchild = 0x0, name = 0xbafcac "TopTransaction"

中层:0x198a520
p *CurrentResourceOwner->parent
{parent = 0x1996c58顶层, firstchild = 0x19a2b40底层, nextchild = 0x0, name = 0xbafd01 "SubTransaction"

底层:0x19a2b40
p *CurrentResourceOwner
{parent = 0x198a520中层, firstchild = 0x0, nextchild = 0x0, name = 0xbafd01 "SubTransaction"
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 顶层ResourceOwner生命周期
  • 2 如何记录资源?
    • 2.1 数据结构
      • 2.2 算法
        • 2.2.1 记录pin buffer
        • 2.2.2 记录lock
    • 3 如何统一释放资源?
    • 4 子事务ResourceOwner层次关系
    相关产品与服务
    数据保险箱
    数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档