mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Scan the buffer pool just once, not once per fork, during relation drop.
This provides a speedup of about 4X when NBuffers is large enough. There is also a useful reduction in sinval traffic, since we only do CacheInvalidateSmgr() once not once per fork. Simon Riggs, reviewed and somewhat revised by Tom Lane
This commit is contained in:
		
							parent
							
								
									5baf6da717
								
							
						
					
					
						commit
						ece01aae47
					
				| @ -1356,12 +1356,8 @@ FinishPreparedTransaction(const char *gid, bool isCommit) | |||||||
| 	for (i = 0; i < ndelrels; i++) | 	for (i = 0; i < ndelrels; i++) | ||||||
| 	{ | 	{ | ||||||
| 		SMgrRelation srel = smgropen(delrels[i], InvalidBackendId); | 		SMgrRelation srel = smgropen(delrels[i], InvalidBackendId); | ||||||
| 		ForkNumber	fork; |  | ||||||
| 
 | 
 | ||||||
| 		for (fork = 0; fork <= MAX_FORKNUM; fork++) | 		smgrdounlink(srel, false); | ||||||
| 		{ |  | ||||||
| 			smgrdounlink(srel, fork, false); |  | ||||||
| 		} |  | ||||||
| 		smgrclose(srel); | 		smgrclose(srel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4638,10 +4638,8 @@ xact_redo_commit_internal(TransactionId xid, XLogRecPtr lsn, | |||||||
| 		ForkNumber	fork; | 		ForkNumber	fork; | ||||||
| 
 | 
 | ||||||
| 		for (fork = 0; fork <= MAX_FORKNUM; fork++) | 		for (fork = 0; fork <= MAX_FORKNUM; fork++) | ||||||
| 		{ |  | ||||||
| 			XLogDropRelation(xnodes[i], fork); | 			XLogDropRelation(xnodes[i], fork); | ||||||
| 			smgrdounlink(srel, fork, true); | 		smgrdounlink(srel, true); | ||||||
| 		} |  | ||||||
| 		smgrclose(srel); | 		smgrclose(srel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -4778,10 +4776,8 @@ xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid) | |||||||
| 		ForkNumber	fork; | 		ForkNumber	fork; | ||||||
| 
 | 
 | ||||||
| 		for (fork = 0; fork <= MAX_FORKNUM; fork++) | 		for (fork = 0; fork <= MAX_FORKNUM; fork++) | ||||||
| 		{ |  | ||||||
| 			XLogDropRelation(xlrec->xnodes[i], fork); | 			XLogDropRelation(xlrec->xnodes[i], fork); | ||||||
| 			smgrdounlink(srel, fork, true); | 		smgrdounlink(srel, true); | ||||||
| 		} |  | ||||||
| 		smgrclose(srel); | 		smgrclose(srel); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -356,13 +356,9 @@ smgrDoPendingDeletes(bool isCommit) | |||||||
| 			if (pending->atCommit == isCommit) | 			if (pending->atCommit == isCommit) | ||||||
| 			{ | 			{ | ||||||
| 				SMgrRelation srel; | 				SMgrRelation srel; | ||||||
| 				int			i; |  | ||||||
| 
 | 
 | ||||||
| 				srel = smgropen(pending->relnode, pending->backend); | 				srel = smgropen(pending->relnode, pending->backend); | ||||||
| 				for (i = 0; i <= MAX_FORKNUM; i++) | 				smgrdounlink(srel, false); | ||||||
| 				{ |  | ||||||
| 					smgrdounlink(srel, i, false); |  | ||||||
| 				} |  | ||||||
| 				smgrclose(srel); | 				smgrclose(srel); | ||||||
| 			} | 			} | ||||||
| 			/* must explicitly free the list entry */ | 			/* must explicitly free the list entry */ | ||||||
|  | |||||||
| @ -2020,7 +2020,7 @@ BufferIsPermanent(Buffer buffer) | |||||||
|  *		DropRelFileNodeBuffers |  *		DropRelFileNodeBuffers | ||||||
|  * |  * | ||||||
|  *		This function removes from the buffer pool all the pages of the |  *		This function removes from the buffer pool all the pages of the | ||||||
|  *		specified relation that have block numbers >= firstDelBlock. |  *		specified relation fork that have block numbers >= firstDelBlock. | ||||||
|  *		(In particular, with firstDelBlock = 0, all pages are removed.) |  *		(In particular, with firstDelBlock = 0, all pages are removed.) | ||||||
|  *		Dirty pages are simply dropped, without bothering to write them |  *		Dirty pages are simply dropped, without bothering to write them | ||||||
|  *		out first.	Therefore, this is NOT rollback-able, and so should be |  *		out first.	Therefore, this is NOT rollback-able, and so should be | ||||||
| @ -2089,6 +2089,46 @@ DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum, | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* ---------------------------------------------------------------------
 | ||||||
|  |  *		DropRelFileNodeAllBuffers | ||||||
|  |  * | ||||||
|  |  *		This function removes from the buffer pool all the pages of all | ||||||
|  |  *		forks of the specified relation.  It's equivalent to calling | ||||||
|  |  *		DropRelFileNodeBuffers once per fork with firstDelBlock = 0. | ||||||
|  |  * -------------------------------------------------------------------- | ||||||
|  |  */ | ||||||
|  | void | ||||||
|  | DropRelFileNodeAllBuffers(RelFileNodeBackend rnode) | ||||||
|  | { | ||||||
|  | 	int			i; | ||||||
|  | 
 | ||||||
|  | 	/* If it's a local relation, it's localbuf.c's problem. */ | ||||||
|  | 	if (rnode.backend != InvalidBackendId) | ||||||
|  | 	{ | ||||||
|  | 		if (rnode.backend == MyBackendId) | ||||||
|  | 			DropRelFileNodeAllLocalBuffers(rnode.node); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < NBuffers; i++) | ||||||
|  | 	{ | ||||||
|  | 		volatile BufferDesc *bufHdr = &BufferDescriptors[i]; | ||||||
|  | 
 | ||||||
|  | 		/*
 | ||||||
|  | 		 * As in DropRelFileNodeBuffers, an unlocked precheck should be safe | ||||||
|  | 		 * and saves some cycles. | ||||||
|  | 		 */ | ||||||
|  | 		if (!RelFileNodeEquals(bufHdr->tag.rnode, rnode.node)) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		LockBufHdr(bufHdr); | ||||||
|  | 		if (RelFileNodeEquals(bufHdr->tag.rnode, rnode.node)) | ||||||
|  | 			InvalidateBuffer(bufHdr);	/* releases spinlock */ | ||||||
|  | 		else | ||||||
|  | 			UnlockBufHdr(bufHdr); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* ---------------------------------------------------------------------
 | /* ---------------------------------------------------------------------
 | ||||||
|  *		DropDatabaseBuffers |  *		DropDatabaseBuffers | ||||||
|  * |  * | ||||||
|  | |||||||
| @ -330,6 +330,46 @@ DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum, | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * DropRelFileNodeAllLocalBuffers | ||||||
|  |  *		This function removes from the buffer pool all pages of all forks | ||||||
|  |  *		of the specified relation. | ||||||
|  |  * | ||||||
|  |  *		See DropRelFileNodeAllBuffers in bufmgr.c for more notes. | ||||||
|  |  */ | ||||||
|  | void | ||||||
|  | DropRelFileNodeAllLocalBuffers(RelFileNode rnode) | ||||||
|  | { | ||||||
|  | 	int			i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < NLocBuffer; i++) | ||||||
|  | 	{ | ||||||
|  | 		BufferDesc *bufHdr = &LocalBufferDescriptors[i]; | ||||||
|  | 		LocalBufferLookupEnt *hresult; | ||||||
|  | 
 | ||||||
|  | 		if ((bufHdr->flags & BM_TAG_VALID) && | ||||||
|  | 			RelFileNodeEquals(bufHdr->tag.rnode, rnode)) | ||||||
|  | 		{ | ||||||
|  | 			if (LocalRefCount[i] != 0) | ||||||
|  | 				elog(ERROR, "block %u of %s is still referenced (local %u)", | ||||||
|  | 					 bufHdr->tag.blockNum, | ||||||
|  | 					 relpathbackend(bufHdr->tag.rnode, MyBackendId, | ||||||
|  | 									bufHdr->tag.forkNum), | ||||||
|  | 					 LocalRefCount[i]); | ||||||
|  | 			/* Remove entry from hashtable */ | ||||||
|  | 			hresult = (LocalBufferLookupEnt *) | ||||||
|  | 				hash_search(LocalBufHash, (void *) &bufHdr->tag, | ||||||
|  | 							HASH_REMOVE, NULL); | ||||||
|  | 			if (!hresult)		/* shouldn't happen */ | ||||||
|  | 				elog(ERROR, "local buffer hash table corrupted"); | ||||||
|  | 			/* Mark buffer invalid */ | ||||||
|  | 			CLEAR_BUFFERTAG(bufHdr->tag); | ||||||
|  | 			bufHdr->flags = 0; | ||||||
|  | 			bufHdr->usage_count = 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * InitLocalBuffers - |  * InitLocalBuffers - | ||||||
|  *	  init the local buffer cache. Since most queries (esp. multi-user ones) |  *	  init the local buffer cache. Since most queries (esp. multi-user ones) | ||||||
|  | |||||||
| @ -329,7 +329,64 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  *	smgrdounlink() -- Immediately unlink a relation. |  *	smgrdounlink() -- Immediately unlink all forks of a relation. | ||||||
|  |  * | ||||||
|  |  *		All forks of the relation 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. | ||||||
|  |  * | ||||||
|  |  *		This is equivalent to calling smgrdounlinkfork for each fork, but | ||||||
|  |  *		it's significantly quicker so should be preferred when possible. | ||||||
|  |  */ | ||||||
|  | void | ||||||
|  | smgrdounlink(SMgrRelation reln, bool isRedo) | ||||||
|  | { | ||||||
|  | 	RelFileNodeBackend rnode = reln->smgr_rnode; | ||||||
|  | 	int			which = reln->smgr_which; | ||||||
|  | 	ForkNumber	forknum; | ||||||
|  | 
 | ||||||
|  | 	/* Close the forks at smgr level */ | ||||||
|  | 	for (forknum = 0; forknum <= MAX_FORKNUM; forknum++) | ||||||
|  | 		(*(smgrsw[which].smgr_close)) (reln, forknum); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Get rid of any remaining buffers for the relation.  bufmgr will just | ||||||
|  | 	 * drop them without bothering to write the contents. | ||||||
|  | 	 */ | ||||||
|  | 	DropRelFileNodeAllBuffers(rnode); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * It'd be nice to tell the stats collector to forget it immediately, too. | ||||||
|  | 	 * But we can't because we don't know the OID (and in cases involving | ||||||
|  | 	 * relfilenode swaps, it's not always clear which table OID to forget, | ||||||
|  | 	 * anyway). | ||||||
|  | 	 */ | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Send a shared-inval message to force other backends to close any | ||||||
|  | 	 * dangling smgr references they may have for this rel.  We should do this | ||||||
|  | 	 * before starting the actual unlinking, in case we fail partway through | ||||||
|  | 	 * that step.  Note that the sinval message will eventually come back to | ||||||
|  | 	 * this backend, too, and thereby provide a backstop that we closed our | ||||||
|  | 	 * own smgr rel. | ||||||
|  | 	 */ | ||||||
|  | 	CacheInvalidateSmgr(rnode); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * 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. | ||||||
|  | 	 */ | ||||||
|  | 	for (forknum = 0; forknum <= MAX_FORKNUM; forknum++) | ||||||
|  | 		(*(smgrsw[which].smgr_unlink)) (rnode, forknum, isRedo); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  *	smgrdounlinkfork() -- Immediately unlink one fork of a relation. | ||||||
|  * |  * | ||||||
|  *		The specified fork of the relation is removed from the store.  This |  *		The specified fork of the relation is removed from the store.  This | ||||||
|  *		should not be used during transactional operations, since it can't be |  *		should not be used during transactional operations, since it can't be | ||||||
| @ -339,16 +396,16 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo) | |||||||
|  *		already. |  *		already. | ||||||
|  */ |  */ | ||||||
| void | void | ||||||
| smgrdounlink(SMgrRelation reln, ForkNumber forknum, bool isRedo) | smgrdounlinkfork(SMgrRelation reln, ForkNumber forknum, bool isRedo) | ||||||
| { | { | ||||||
| 	RelFileNodeBackend rnode = reln->smgr_rnode; | 	RelFileNodeBackend rnode = reln->smgr_rnode; | ||||||
| 	int			which = reln->smgr_which; | 	int			which = reln->smgr_which; | ||||||
| 
 | 
 | ||||||
| 	/* Close the fork */ | 	/* Close the fork at smgr level */ | ||||||
| 	(*(smgrsw[which].smgr_close)) (reln, forknum); | 	(*(smgrsw[which].smgr_close)) (reln, forknum); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Get rid of any remaining buffers for the relation.  bufmgr will just | 	 * Get rid of any remaining buffers for the fork.  bufmgr will just | ||||||
| 	 * drop them without bothering to write the contents. | 	 * drop them without bothering to write the contents. | ||||||
| 	 */ | 	 */ | ||||||
| 	DropRelFileNodeBuffers(rnode, forknum, 0); | 	DropRelFileNodeBuffers(rnode, forknum, 0); | ||||||
|  | |||||||
| @ -210,6 +210,7 @@ extern BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, | |||||||
| extern void MarkLocalBufferDirty(Buffer buffer); | extern void MarkLocalBufferDirty(Buffer buffer); | ||||||
| extern void DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum, | extern void DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum, | ||||||
| 							BlockNumber firstDelBlock); | 							BlockNumber firstDelBlock); | ||||||
|  | extern void DropRelFileNodeAllLocalBuffers(RelFileNode rnode); | ||||||
| extern void AtEOXact_LocalBuffers(bool isCommit); | extern void AtEOXact_LocalBuffers(bool isCommit); | ||||||
| 
 | 
 | ||||||
| #endif   /* BUFMGR_INTERNALS_H */ | #endif   /* BUFMGR_INTERNALS_H */ | ||||||
|  | |||||||
| @ -188,6 +188,7 @@ extern void FlushRelationBuffers(Relation rel); | |||||||
| extern void FlushDatabaseBuffers(Oid dbid); | extern void FlushDatabaseBuffers(Oid dbid); | ||||||
| extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode, | extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode, | ||||||
| 					   ForkNumber forkNum, BlockNumber firstDelBlock); | 					   ForkNumber forkNum, BlockNumber firstDelBlock); | ||||||
|  | extern void DropRelFileNodeAllBuffers(RelFileNodeBackend rnode); | ||||||
| extern void DropDatabaseBuffers(Oid dbid); | extern void DropDatabaseBuffers(Oid dbid); | ||||||
| 
 | 
 | ||||||
| #define RelationGetNumberOfBlocks(reln) \ | #define RelationGetNumberOfBlocks(reln) \ | ||||||
|  | |||||||
| @ -80,8 +80,8 @@ extern void smgrclose(SMgrRelation reln); | |||||||
| extern void smgrcloseall(void); | extern void smgrcloseall(void); | ||||||
| extern void smgrclosenode(RelFileNodeBackend rnode); | extern void smgrclosenode(RelFileNodeBackend rnode); | ||||||
| extern void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo); | extern void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo); | ||||||
| extern void smgrdounlink(SMgrRelation reln, ForkNumber forknum, | extern void smgrdounlink(SMgrRelation reln, bool isRedo); | ||||||
| 			 bool isRedo); | extern void smgrdounlinkfork(SMgrRelation reln, ForkNumber forknum, bool isRedo); | ||||||
| extern void smgrextend(SMgrRelation reln, ForkNumber forknum, | extern void smgrextend(SMgrRelation reln, ForkNumber forknum, | ||||||
| 		   BlockNumber blocknum, char *buffer, bool skipFsync); | 		   BlockNumber blocknum, char *buffer, bool skipFsync); | ||||||
| extern void smgrprefetch(SMgrRelation reln, ForkNumber forknum, | extern void smgrprefetch(SMgrRelation reln, ForkNumber forknum, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user