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


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

    速查:

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

    1 顶层ResourceOwner生命周期

    单SQL执行为例:
    在这里插入图片描述
    注意申请的两个ResourceOwner会有层次关系,最外层套的是TopTransaction,内层是Portal。

            CurrentResourceOwner
                       |
                       |
                       v
    {parent = 0x1996c58, firstchild = 0x0, nextchild = 0x0, name = 0xd47445 "Portal", ... }
        |              ^
        |              |
        v              |
    {parent = 0x0, firstchild = 0x198a520, nextchild = 0x0, name = 0xbafcac "TopTransaction", ... }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2 如何记录资源?

    2.1 数据结构

    1. 每种资源对应一个数组:ResourceArray。
    2. 数组设计成了通用数据存储空间,几个变量的作用:itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity.
    3. 数组达到一定大小后(最大64),后转换为开放地址哈希表。
    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;
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2.2 算法

    2.2.1 记录pin buffer
    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以记录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向后搜索使用。
    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++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    2.2.2 记录lock

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

    typedef struct ResourceOwnerData
    {
    ...
        int nlocks; /* number of owned locks */
        LOCALLOCK *locks[15]; /* list of owned locks */
    } ResourceOwnerData;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    记录常规锁:

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

    删除记录常规锁:

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

    3 如何统一释放资源?

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

    ResourceOwnerRelease
      ResourceOwnerReleaseInternal
        	for (child = owner->firstchild; child != NULL; child = child->nextchild)
    		  ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
            ..
            ..
            【干活】
            ...
            ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    【干活】:

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

    typedef enum
    {
    	RESOURCE_RELEASE_BEFORE_LOCKS,
    	RESOURCE_RELEASE_LOCKS,
    	RESOURCE_RELEASE_AFTER_LOCKS
    } ResourceReleasePhase;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ResourceOwnerRelease具体工作:

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

    • 1 拿到当前数组记录的所有资源项
    • 2 调用当前资源回收函数进行回收
    ...
    		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
    		{
    			Buffer		res = DatumGetBuffer(foundres);
    			ReleaseBuffer(res);
    		}
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4 子事务ResourceOwner层次关系

    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);
    
    ...
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    顶层: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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    教你如何玩转海外版抖音TikTok跨境电商独立站
    工业物联网案例:智能工厂设备无人值守系统方案
    Python中的多态和封装是如何实现的
    深入解析Spring Boot中最常用注解的使用方式(上篇)
    U2-Net显著性检测源码详解
    安卓BLE开发介绍
    【vue+nestjs】qq第三方授权登录【超详细】
    2023贵州财经大学计算机考研信息汇总
    【docker桌面版】windows使用docker搭建nginx
    Redis 的6种回收策略(淘汰策略)详解
  • 原文地址:https://blog.csdn.net/jackgo73/article/details/126386052