保定定兴网站建设,闵行区核酸检测点,男女在床上做暖暖插孔视频网站,网站建设增值服务0 总结
三段释放原因#xff1a;因为如果先释放锁#xff0c;没有释放一些共享资源#xff08;比如pin住的buffer#xff09;#xff0c;别人拿到锁后发现我们仍然持有一些资源#xff0c;就会有问题。所以三阶段释放主要是以锁为分界线#xff0c;先释放锁保护的资源因为如果先释放锁没有释放一些共享资源比如pin住的buffer别人拿到锁后发现我们仍然持有一些资源就会有问题。所以三阶段释放主要是以锁为分界线先释放锁保护的资源在释放锁在清理私有资源。这样可以保证别人拿到锁后一定也能拿到对应的资源。三段先放pinned buffer、relation、dsm这些共享资源再放锁再放其他会话看不到的私有资源。
1 资源随事务释放
三阶段释放是指ResourceOwnerRelease函数在使用时需要调用三次按固定顺序调用每次删除特定的资源
RESOURCE_RELEASE_BEFORE_LOCKSRESOURCE_RELEASE_LOCKSRESOURCE_RELEASE_AFTER_LOCKS
typedef enum
{RESOURCE_RELEASE_BEFORE_LOCKS 1,RESOURCE_RELEASE_LOCKS,RESOURCE_RELEASE_AFTER_LOCKS,
} ResourceReleasePhase;例如事务提交时CommitTransaction
static void
CommitTransaction(void).........ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_BEFORE_LOCKS,true, true);/* Check weve released all buffer pins */AtEOXact_Buffers(true);/* Clean up the relation cache */AtEOXact_RelationCache(true);/** Make catalog changes visible to all backends. This has to happen after* relcache references are dropped (see comments for* AtEOXact_RelationCache), but before locks are released (if anyone is* waiting for lock on a relation weve modified, we want them to know* about the catalog change before they start using the relation).*/AtEOXact_Inval(true);AtEOXact_MultiXact();ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_LOCKS,true, true);ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_AFTER_LOCKS,true, true);......其他几个事务控制函数资源释放时也是按照相同的顺序分三阶段释放的
函数名phaseisCommitisTopLevelCommitTransactionRESOURCE_RELEASE_BEFORE_LOCKStruetrueCommitTransactionRESOURCE_RELEASE_LOCKStruetrueCommitTransactionRESOURCE_RELEASE_AFTER_LOCKStruetruePrepareTransactionRESOURCE_RELEASE_BEFORE_LOCKStruetruePrepareTransactionRESOURCE_RELEASE_LOCKStruetruePrepareTransactionRESOURCE_RELEASE_AFTER_LOCKStruetrueAbortTransactionRESOURCE_RELEASE_BEFORE_LOCKSfalsetrueAbortTransactionRESOURCE_RELEASE_LOCKSfalsetrueAbortTransactionRESOURCE_RELEASE_AFTER_LOCKSfalsetrueCommitSubTransactionRESOURCE_RELEASE_BEFORE_LOCKStruefalseCommitSubTransactionRESOURCE_RELEASE_LOCKStruefalseCommitSubTransactionRESOURCE_RELEASE_AFTER_LOCKStruefalseAbortSubTransactionRESOURCE_RELEASE_BEFORE_LOCKSfalsefalseAbortSubTransactionRESOURCE_RELEASE_LOCKSfalsefalseAbortSubTransactionRESOURCE_RELEASE_AFTER_LOCKSfalsefalse
2 为什么要分三阶段释放
结论
因为如果先释放锁没有释放一些共享资源比如pin住的buffer别人拿到锁后发现我们仍然持有一些资源就会有问题。所以三阶段释放主要是以锁为分界线先释放锁保护的资源在释放锁在清理私有资源。这样可以保证别人拿到锁后一定也能拿到对应的资源。
RESOURCE_RELEASE_BEFORE_LOCKS预锁定阶段需要释放对其他后端可见的资源pinned buffers。为了确保当我们释放另一个后端可能正在等待的锁时它会看到我们已经完全退出了我们的事务。这是为了防止在释放锁之后其他后端仍然看到我们持有的资源从而可能导致数据不一致或其他问题。RESOURCE_RELEASE_LOCKS锁定阶段释放持有的所有锁。这是为了让其他可能正在等待这些锁的后端能够继续执行。RESOURCE_RELEASE_AFTER_LOCKS后锁定阶段后端内部的清理工作。释放那些只有后端自己知道的、不会影响其他后端的资源。
3 代码分析
下面这次提交后对resowner做了扩展性增强代码逻辑没变但可读性有点差PG17dev分支
commit b8bff07daa85c837a2747b4d35cd5a27e73fb7b2
Author: Heikki Linnakangas heikki.linnakangasiki.fi
Date: Wed Nov 8 13:30:50 2023 0200Make ResourceOwners more easily extensible.围绕本篇主题后面分析这次提交前代码PG17前的逻辑
static void
ResourceOwnerReleaseInternal(ResourceOwner owner,ResourceReleasePhase phase,bool isCommit,bool isTopLevel)
{ResourceOwner child;ResourceOwner save;ResourceReleaseCallbackItem *item;ResourceReleaseCallbackItem *next;Datum foundres;resowner维护树形结构递归释放自己的child资源。 for (child owner-firstchild; child ! NULL; child child-nextchild)ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);...
3.1 第一阶段RESOURCE_RELEASE_BEFORE_LOCKS
事务提交时不应该有正在进行中的io了这里需要把标志位清理干净避免影响后面事务使用这个buffer。清理pinned的buffer 提交时不应该有pinned的了会告警回滚时就无所谓了因为错误不一定出在哪不一定给机会清理。 关闭打开的relation 提交时不应该有打开的了会告警回滚时同上。 关闭dsm段关闭jit context if (phase RESOURCE_RELEASE_BEFORE_LOCKS){while (ResourceArrayGetAny((owner-bufferioarr), foundres)){Buffer res DatumGetBuffer(foundres);if (isCommit)elog(PANIC, lost track of buffer IO on buffer %d, res);AbortBufferIO(res);}while (ResourceArrayGetAny((owner-bufferarr), foundres)){Buffer res DatumGetBuffer(foundres);if (isCommit)PrintBufferLeakWarning(res);ReleaseBuffer(res);}while (ResourceArrayGetAny((owner-relrefarr), foundres)){Relation res (Relation) DatumGetPointer(foundres);if (isCommit)PrintRelCacheLeakWarning(res);RelationClose(res);}while (ResourceArrayGetAny((owner-dsmarr), foundres)){dsm_segment *res (dsm_segment *) DatumGetPointer(foundres);if (isCommit)PrintDSMLeakWarning(res);dsm_detach(res);}while (ResourceArrayGetAny((owner-jitarr), foundres)){JitContext *context (JitContext *) DatumGetPointer(foundres);jit_release_context(context);}......}}3.2 第二阶段RESOURCE_RELEASE_LOCKS
如果是顶层事务直接释放所有锁具体 提交时要保留会话锁释放事务锁。事务没了会话锁还需要继续生效生命周期比事务长。回滚时要释放所有锁。会话锁咨询锁。事务锁行锁、表锁等。 如果是子事务按提交回滚做出不同行为。 提交转移锁到parent resowner。回滚释放锁。 else if (phase RESOURCE_RELEASE_LOCKS){if (isTopLevel){if (owner TopTransactionResourceOwner){ProcReleaseLocks(isCommit);ReleasePredicateLocks(isCommit, false);}}else{LOCALLOCK **locks;int nlocks;...if (isCommit)LockReassignCurrentOwner(locks, nlocks);elseLockReleaseCurrentOwner(locks, nlocks);}}3.3 第三阶段RESOURCE_RELEASE_AFTER_LOCKS
释放catcache系统表缓存。释放cached plan。释放tuple desc注意tuple desc在构造好后会用IncrTupleDescRefCount函数在resowner中记录按引用计数控制删除。引用计数的机制主要是为了处理TupleDesc在多个地方共享使用的情况。例如一个查询的多个部分可能都需要引用同一个TupleDesc。如果在一个部分结束时就直接删除TupleDesc那么其他部分就无法继续使用这个TupleDesc了。释放快照。释放fd。 else if (phase RESOURCE_RELEASE_AFTER_LOCKS){while (ResourceArrayGetAny((owner-catrefarr), foundres)){HeapTuple res (HeapTuple) DatumGetPointer(foundres);if (isCommit)PrintCatCacheLeakWarning(res);ReleaseCatCache(res);}while (ResourceArrayGetAny((owner-catlistrefarr), foundres)){CatCList *res (CatCList *) DatumGetPointer(foundres);if (isCommit)PrintCatCacheListLeakWarning(res);ReleaseCatCacheList(res);}while (ResourceArrayGetAny((owner-planrefarr), foundres)){CachedPlan *res (CachedPlan *) DatumGetPointer(foundres);if (isCommit)PrintPlanCacheLeakWarning(res);ReleaseCachedPlan(res, owner);}while (ResourceArrayGetAny((owner-tupdescarr), foundres)){TupleDesc res (TupleDesc) DatumGetPointer(foundres);if (isCommit)PrintTupleDescLeakWarning(res);DecrTupleDescRefCount(res);}while (ResourceArrayGetAny((owner-snapshotarr), foundres)){Snapshot res (Snapshot) DatumGetPointer(foundres);if (isCommit)PrintSnapshotLeakWarning(res);UnregisterSnapshot(res);}/* Ditto for temporary files */while (ResourceArrayGetAny((owner-filearr), foundres)){File res DatumGetFile(foundres);if (isCommit)PrintFileLeakWarning(res);FileClose(res);}}for (item ResourceRelease_callbacks; item; item next){next item-next;item-callback(phase, isCommit, isTopLevel, item-arg);}CurrentResourceOwner save;
}