postgres 源码解析14 Drop_table--3

知识回顾
postgres Drop_table源码解析-1
postgres Drop_table源码解析-2
  通过之前的分析可知,删除的表在事务执行的过程中并不删除,而是等事务提交的才将物理文件中磁盘中移除,相应的调用接口函数为smgrDoPendingDeletes:

数据结构

/*
 * We keep a list of all relations (represented as RelFileNode values)
 * that have been created or deleted in the current transaction.  When
 * a relation is created, we create the physical file immediately, but
 * remember it so that we can delete the file again if the current
 * transaction is aborted.  Conversely, a deletion request is NOT
 * executed immediately, but is just entered in the list.  When and if
 * the transaction commits, we can delete the physical file.
 *
 * To handle subtransactions, every entry is marked with its transaction
 * nesting level.  At subtransaction commit, we reassign the subtransaction's
 * entries to the parent nesting level.  At subtransaction abort, we can
 * immediately execute the abort-time actions for all entries of the current
 * nesting level.
 *
 * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
 * unbetimes.  It'd probably be OK to keep it in TopTransactionContext,
 * but I'm being paranoid.
 */

typedef struct PendingRelDelete
{
	RelFileNode relnode;		/* relation that may need to be deleted */   // 物理文件地址
	BackendId	backend;		/* InvalidBackendId if not a temp rel */  
	bool		atCommit;		/* T=delete at commit; F=delete at abort */
	int			nestLevel;		/* xact nesting level of request */
	struct PendingRelDelete *next;	/* linked-list link */                  // 待删除文件链表 
} PendingRelDelete;

案例:

postgres=# drop table test_1;

函数栈

(gdb) bt 
#0  smgrDoPendingDeletes (isCommit=true) at storage.c:598
#1  0x000000000054ac74 in CommitTransaction () at xact.c:2270
#2  0x000000000054b7b4 in CommitTransactionCommand () at xact.c:2975
#3  0x00000000008fe53d in finish_xact_command () at postgres.c:2721
#4  0x00000000008fc199 in exec_simple_query (query_string=0x188b058 "drop table test_1;") at postgres.c:1239
#5  0x00000000009003f8 in PostgresMain (argc=1, argv=0x7ffdf4801c20, dbname=0x18b6c28 "postgres", username=0x1887bd8 "postgres")
    at postgres.c:4486
#6  0x0000000000851ca3 in BackendRun (port=0x18ade30) at postmaster.c:4506
#7  0x0000000000851629 in BackendStartup (port=0x18ade30) at postmaster.c:4228
#8  0x000000000084dd48 in ServerLoop () at postmaster.c:1745
#9  0x000000000084d629 in PostmasterMain (argc=1, argv=0x1885ba0) at postmaster.c:1417
#10 0x000000000075ec02 in main (argc=1, argv=0x1885ba0) at main.c:209

源代码分析

1 smgrDoPendingDeletes接口介绍:

/*
	 * Likewise, dropping of files deleted during the transaction is best done
	 * after releasing relcache and buffer pins.  (This is not strictly
	 * necessary during commit, since such pins should have been released
	 * already, but this ordering is definitely critical during abort.)  Since
	 * this may take many seconds, also delay until after releasing locks.
	 * Other backends will observe the attendant catalog changes and not
	 * attempt to access affected files.
	 */
	 // 在事务释放relcache和buffe pins后删除文件(在事务提交时顺序不必这么严格,因为之前获取的buffer
	 //	pin 已经释放,但在事务abort时此顺序很关键)。基于第二点,将其推迟至锁释放。其他的backend将只会
	 // 到关于此表的无效元数据信息,不会访问此文件 
	smgrDoPendingDeletes(true);

具体实现:

/*
 *	smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
 *
 * This also runs when aborting a subxact; we want to clean up a failed
 * subxact immediately.
 *
 * Note: It's possible that we're being asked to remove a relation that has
 * no physical storage in any fork. In particular, it's possible that we're
 * cleaning up an old temporary relation for which RemovePgTempFiles has
 * already recovered the physical storage.
 */
void
smgrDoPendingDeletes(bool isCommit)
{
	int			nestLevel = GetCurrentTransactionNestLevel();   // 获取当前事务NestLevel 
	PendingRelDelete *pending;
	PendingRelDelete *prev;
	PendingRelDelete *next;
	int			nrels = 0,
				maxrels = 0;
	SMgrRelation *srels = NULL;

	prev = NULL;
	// pendingDeletes:链表结构,存放待删除的relation信息

	for (pending = pendingDeletes; pending != NULL; pending = next)
	{
		next = pending->next;
		if (pending->nestLevel < nestLevel)
		{
			/* outer-level entries should not be processed yet */ 
			prev = pending;
		}
		else
		{
			/* unlink list entry first, so we don't retry on failure */
			if (prev)
				prev->next = next;
			else
				pendingDeletes = next;
			/* do deletion if called for */
			if (pending->atCommit == isCommit) 
			{
				SMgrRelation srel;

				srel = smgropen(pending->relnode, pending->backend);  // 打开物理文件
				
				// 如有必要初始化SMgrRelation数组,初始大小为8, 如实际大小超于此值,常规法 2倍 扩容
				/* allocate the initial array, or extend it, if needed */
				if (maxrels == 0)
				{
					maxrels = 8;
					srels = palloc(sizeof(SMgrRelation) * maxrels);
				}
				else if (maxrels <= nrels)
				{
					maxrels *= 2;
					srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
				}

				srels[nrels++] = srel;
			}
			/* must explicitly free the list entry */
			pfree(pending);
			/* prev does not change */
		}
	}
    // 统计此事务删除文件数,调用 smgrdounlinkall 
	if (nrels > 0)
	{
		smgrdounlinkall(srels, nrels, false);

		for (int i = 0; i < nrels; i++)
			smgrclose(srels[i]);

		pfree(srels);
	}
}

2 smgrdounlinkall接口函数

/*
 *	smgrdounlinkall() -- Immediately unlink all forks of all given relations
 *
 *		All forks of all given relations are removed from the store.  This
 *		should not be used during transactional operations, since it can't be
 *		undone.
 *
 *		If isRedo is true, it is okay for the underlying file(s) to be gone
 *		already.
 */
void
smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
{
	int			i = 0;
	RelFileNodeBackend *rnodes;
	ForkNumber	forknum;

	if (nrels == 0)
		return;

	/*
	 * Get rid of any remaining buffers for the relations.  bufmgr will just
	 * drop them without bothering to write the contents.
	 */
	 // 首先释放涉及rels的所有数据缓冲区 buffer pool 
	DropRelFileNodesAllBuffers(rels, nrels);

	/*
	 * create an array which contains all relations to be dropped, and close
	 * each relation's forks at the smgr level while at it
	 */
	 // 创建包含所有待drop的relations 数组,并关闭涉及的所有分支文件 [普通表,FSM表, VM表]
	rnodes = palloc(sizeof(RelFileNodeBackend) * nrels);
	for (i = 0; i < nrels; i++)
	{
		RelFileNodeBackend rnode = rels[i]->smgr_rnode;
		int			which = rels[i]->smgr_which;

		rnodes[i] = rnode;

		/* Close the forks at smgr level */
		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
			smgrsw[which].smgr_close(rels[i], forknum);
	}

	/*
	 * It'd be nice to tell the stats collector to forget them immediately,
	 * too. But we can't because we don't know the OIDs.
	 */

	/*
	 * Send a shared-inval message to force other backends to close any
	 * dangling smgr references they may have for these rels.  We should do
	 * this before starting the actual unlinking, in case we fail partway
	 * through that step.  Note that the sinval messages will eventually come
	 * back to this backend, too, and thereby provide a backstop that we
	 * closed our own smgr rel.
	 */
	 // 向其他backend发送消息迫使其关闭任何与待删除relations的 smgr引用
	 
	for (i = 0; i < nrels; i++)
		CacheInvalidateSmgr(rnodes[i]);

	/*
	 * Delete the physical file(s).
	 *
	 * Note: smgr_unlink must treat deletion failure as a WARNING, not an
	 * ERROR, because we've already decided to commit or abort the current
	 * xact.
	 */
      // 调用smgr_unlink进行物理删除
	for (i = 0; i < nrels; i++)
	{
		int			which = rels[i]->smgr_which;

		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
			smgrsw[which].smgr_unlink(rnodes[i], forknum, isRedo);
	}

	pfree(rnodes);
}

3 smgr_unlink函数进行了多次封装,其最终调用truncate系统函数实现物理删除,该函数的功能是将指定文件阶截断至字节数为0大小,文件其实还存在,只是大小为0;

/* Truncate FILE to LENGTH bytes.  */
# ifndef __USE_FILE_OFFSET64
extern int truncate (const char *__file, __off_t __length)
     __THROW __nonnull ((1)) __wur;

函数栈如下,有兴趣可以阅读相关源代码

(gdb) bt 
#0  pg_truncate (path=0x18b0fe8 "base/13835/131111_vm", length=0) at fd.c:655
#1  0x00000000008f6e5a in do_truncate (path=0x18b0fe8 "base/13835/131111_vm") at md.c:298
#2  0x00000000008f6f2b in mdunlinkfork (rnode=..., forkNum=VISIBILITYMAP_FORKNUM, isRedo=false) at md.c:329
#3  0x00000000008f6e3b in mdunlink (rnode=..., forkNum=VISIBILITYMAP_FORKNUM, isRedo=false) at md.c:286
#4  0x00000000008f91fb in smgrdounlinkall (rels=0x18b2a28, nrels=1, isRedo=false) at smgr.c:445

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值