c语言sscanf函数的用法是什么
348
2022-12-02
PostgreSQL数据库事务系统——ResourceOwner
ResourceOwner对象
在PostgreSQL中,每个事务(包括其子事务)在执行时,需要跟踪其占用的内存资源。为简化查询相关资源的管理(如pin和表级锁等),PG中引入了ResourceOwner对象(例如,需要跟踪一个缓冲区的pin锁或一个表的锁资源,以保障在事务结束或失败的时候能够被及时释放)的概念。这些与查询相关的资源必须以某种可靠的方式被跟踪,以确保当查询结束后被释放,甚至是查询由于错误被中止时资源被释放。相对于期望整个执行过程拥有牢不可破的数据结构,PG更愿意采用单例模式去跟踪这些资源。ResourceOwner记录了大多数事务过程中使用到的资源,主要包括:
父节点、子节点的ResourceOwner指针,用于构成资源跟踪器之间的树状结构占用的共享缓冲区数组和持有的缓冲区pin锁个数Cache的引用次数以及占用的Cache中存储的数据链表,包括CatCache、RelCache以及缓存查询计划的PlanCacheTupleDesc引用次数以及占用的TupleDesc数组Snapshot引用次数以及占用的Snapsot数组
ResourceOwner的API建立在MemoryContext API之上,MemoryContext API已经被证明是十分灵活的,并且在防止内存泄漏上十分有效。另外,ResourceOwner可以有子对象(child ResourceOwner),可以形成对象树,当释放父对象(parent ResourceOwner)时,所有直接和间接的子节点都将被释放。 (看起来十分具有诱惑力,统一ResourceOwner和MemoryContext到一个统一的对象,但他们的使用方式差异很大,实际上并没有多大好处。)每个ResourceOwner的节点内存都在TopMemoryContext中进行分配。在内存中保存了三个全局的ResourceOwner结构变量:
CurrentResourceOwner记录当前使用的ResourceOwnerCurTransactionResourceOwner记录当前事务的ResourceOwnerTopTransactionResourceOwner记录顶层事务的ResourceOwner
src/include/utils/resowner.htypedef struct ResourceOwnerData *ResourceOwner;/* Globally known ResourceOwners */extern PGDLLIMPORT ResourceOwner CurrentResourceOwner;extern PGDLLIMPORT ResourceOwner CurTransactionResourceOwner;extern PGDLLIMPORT ResourceOwner TopTransactionResourceOwner;extern PGDLLIMPORT ResourceOwner AuxProcessResourceOwner;src/backend/utils/resowner/resowner.ctypedef 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 */ /* 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;
每个事务的资源跟踪器都通过其parent指针指向父事务的资源跟踪器,如果该事务本身就是顶层事务,则parent指针为空。同一个事务的子事务的资源跟踪器之间通过nextchild指针连接在一起,而父事务的资源跟踪器的firstchild指针则指向其第一个子事务的资源跟踪器。通过parent、firstchild、nextchild三个指针,资源管理器之间就构成了一种树状结构。
ResourceArray用于存放所有类型的资源ID,将资源ID存放在成员itemsarr数组中(itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity)。如果资源ID过多,我们使用开链哈希表。itemsarr数组是由capacity slot组成的哈希表,每个slot或者存放资源ID或者"invalidval"。nitems是合法的资源ID的数量。如果没有超过maxiems,我们可以增大数组然后重新hash。在这种模式下,maxitems应该小于capacity,这样我们不用花费太多时间搜索空的slot。在这两种模式,lastidex记录的是最后一个插入或者由GetAny返回的位置,这会加快ResourceArrayRemove函数执行。
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;
PG中为每个事务、子事务以及每个Portal创建一个ResourceOwner。在Portal的整个生命周期中,全局变量CurrentResourceOwner指向Portal的ResourceOwner,这有助于类似这样的操作(如ReadBuffer、LockAcquire等)都可以记录所需的资源到这个ResourceOwner中。当Portal关闭时,任何未释放的资源(典型的主要是锁)成为当前事务的责任。这体现在将Portal的ResourceOwner变成当前事务的ResourceOwner的子对象。resowner.c中当释放子对象时,自动传输资源给父对象。同样,子事务的ResourceOwner也是他们直接父对象的子对象。PG既需要事务相关的ResourceOwner也需要Portal相关的ResourceOwner,因为事务可能在尚未有Portal关联时进行要求资源的初始化工作(如查询分析query parsing)。
为每个事务创建一个ResourceOwner
static void AtStart_ResourceOwner(void) { TransactionState s = CurrentTransactionState; /* We shouldn't have a transaction resource owner already. */ Assert(TopTransactionResourceOwner == NULL); /* Create a toplevel resource owner for the transaction. */ s->curTransactionOwner = ResourceOwnerCreate(NULL, "TopTransaction"); TopTransactionResourceOwner = s->curTransactionOwner; CurTransactionResourceOwner = s->curTransactionOwner; CurrentResourceOwner = s->curTransactionOwner;}
为每个子事务创建一个ResourceOwner
static void AtSubStart_ResourceOwner(void) { TransactionState s = CurrentTransactionState; Assert(s->parent != NULL); /* Create a resource owner for the subtransaction. We make it a child of the immediate parent's resource owner. */ s->curTransactionOwner = ResourceOwnerCreate(s->parent->curTransactionOwner, "SubTransaction"); CurTransactionResourceOwner = s->curTransactionOwner; CurrentResourceOwner = s->curTransactionOwner;}
为每个Portal创建一个ResourceOwner
/* CreatePortal Returns a new portal given a name. * allowDup: if true, automatically drop any pre-existing portal of the same name (if false, an error is raised). * dupSilent: if true, don't even emit a WARNING. */Portal CreatePortal(const char *name, bool allowDup, bool dupSilent) { Portal portal; AssertArg(PointerIsValid(name)); portal = GetPortalByName(name); if (PortalIsValid(portal)) { if (!allowDup) ereport(ERROR,(errcode(ERRCODE_DUPLICATE_CURSOR),errmsg("cursor \"%s\" already exists", name))); if (!dupSilent) ereport(WARNING,(errcode(ERRCODE_DUPLICATE_CURSOR),errmsg("closing existing cursor \"%s\"",name))); PortalDrop(portal, false); } /* make new portal structure */ portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof *portal); /* initialize portal context; typically it won't store much */ portal->portalContext = AllocSetContextCreate(TopPortalContext,"PortalContext", ALLOCSET_SMALL_SIZES); /* create a resource owner for the portal */ portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,"Portal"); /* initialize portal fields that don't start off zero */ portal->status = PORTAL_NEW; portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); portal->activeSubid = portal->createSubid; portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; portal->atEnd = true; /* disallow fetches until query is set */ portal->visible = true; portal->creation_time = GetCurrentStatementStartTimestamp(); /* put portal in table (sets portal->name) */ PortalHashTableInsert(portal, name); /* reuse portal->name copy */ MemoryContextSetIdentifier(portal->portalContext, portal->name); return portal;}
API 一览
基本API:
创建ResourceOwner资源关联或解除关联到ResourceOwner释放ResourceOwner的拥有资源(释放所有拥有资源,但不包括对象自身)删除ResourceOwner对象(包括子对象),所有拥有资源必须提前被释放掉
锁被特殊处理,因为在非错误情况下,无论是在子事务中或是Portal申请的锁,锁的持有会直到事务结束。因此,当isCommit为true时,在子对象(child ResourceOwner中的释放操作锁操作会将锁的所有权(lock ownership)传递给父对象,而不是真的释放掉锁。 目前,ResourceOwner直接支持缓冲区pin、lgmr锁、catcache、relcache及tupdesc引用等的属主关系维护,其它对象可通过记录所属ResourceOwner的指针与ResourceOwner进行关联。在其它模块中有这样一个API,当ResourceOwner释放时会得到控制权(get control),来扫描自己的数据以发现需要被删除的对象。
当unpin缓冲区、释放锁秋或cache引用时,当前CurrentResourceOwner必须指向它们申请时相同的ResourceOwner。通过额外的记录可以放松这个限制,但目前来看没有这个必要。代码中,若有CurrentResourceOwner的暂时变化,应使用PG_TRY结构,以确保当出现错误退出时,前一CurrentResourceOwner能正确恢复
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~