vacuum.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,201 行 · 第 1/5 页

C
2,201
字号
	if (num_tuples == 0)		min_tlen = max_tlen = 0;	vacrelstats->min_tlen = min_tlen;	vacrelstats->max_tlen = max_tlen;	vacuum_pages->empty_end_pages = empty_end_pages;	fraged_pages->empty_end_pages = empty_end_pages;	/*	 * Clear the fraged_pages list if we found we couldn't shrink. Else,	 * remove any "empty" end-pages from the list, and compute usable free	 * space = free space in remaining pages.	 */	if (do_shrinking)	{		Assert((BlockNumber) fraged_pages->num_pages >= empty_end_pages);		fraged_pages->num_pages -= empty_end_pages;		usable_free_space = 0;		for (i = 0; i < fraged_pages->num_pages; i++)			usable_free_space += fraged_pages->pagedesc[i]->free;	}	else	{		fraged_pages->num_pages = 0;		usable_free_space = 0;	}	/* don't bother to save vtlinks if we will not call repair_frag */	if (fraged_pages->num_pages > 0 && num_vtlinks > 0)	{		qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData),			  vac_cmp_vtlinks);		vacrelstats->vtlinks = vtlinks;		vacrelstats->num_vtlinks = num_vtlinks;	}	else	{		vacrelstats->vtlinks = NULL;		vacrelstats->num_vtlinks = 0;		pfree(vtlinks);	}	ereport(elevel,			(errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",					RelationGetRelationName(onerel),					tups_vacuumed, num_tuples, nblocks),			 errdetail("%.0f dead row versions cannot be removed yet.\n"				"Nonremovable row versions range from %lu to %lu bytes long.\n"					   "There were %.0f unused item pointers.\n"		 "Total free space (including removable row versions) is %.0f bytes.\n"					   "%u pages are or will become empty, including %u at the end of the table.\n"					   "%u pages containing %.0f free bytes are potential move destinations.\n"					   "%s",					   nkeep,					   (unsigned long) min_tlen, (unsigned long) max_tlen,					   nunused,					   free_space,					   empty_pages, empty_end_pages,					   fraged_pages->num_pages, usable_free_space,					   vac_show_rusage(&ru0))));}/* *	repair_frag() -- try to repair relation's fragmentation * *		This routine marks dead tuples as unused and tries re-use dead space *		by moving tuples (and inserting indexes if needed). It constructs *		Nvacpagelist list of free-ed pages (moved tuples) and clean indexes *		for them after committing (in hack-manner - without losing locks *		and freeing memory!) current transaction. It truncates relation *		if some end-blocks are gone away. */static voidrepair_frag(VRelStats *vacrelstats, Relation onerel,			VacPageList vacuum_pages, VacPageList fraged_pages,			int nindexes, Relation *Irel){	TransactionId myXID;	CommandId	myCID;	Buffer		buf,				cur_buffer;	BlockNumber nblocks,				blkno;	BlockNumber last_move_dest_block = 0,				last_vacuum_block;	Page		page,				ToPage = NULL;	OffsetNumber offnum,				maxoff,				newoff,				max_offset;	ItemId		itemid,				newitemid;	HeapTupleData tuple,				newtup;	TupleDesc	tupdesc;	ResultRelInfo *resultRelInfo;	EState	   *estate;	TupleTable	tupleTable;	TupleTableSlot *slot;	VacPageListData Nvacpagelist;	VacPage		cur_page = NULL,				last_vacuum_page,				vacpage,			   *curpage;	int			cur_item = 0;	int			i;	Size		tuple_len;	int			num_moved,				num_fraged_pages,				vacuumed_pages;	int			checked_moved,				num_tuples,				keep_tuples = 0;	bool		isempty,				dowrite,				chain_tuple_moved;	VacRUsage	ru0;	vac_init_rusage(&ru0);	myXID = GetCurrentTransactionId();	myCID = GetCurrentCommandId();	tupdesc = RelationGetDescr(onerel);	/*	 * We need a ResultRelInfo and an EState so we can use the regular	 * executor's index-entry-making machinery.	 */	estate = CreateExecutorState();	resultRelInfo = makeNode(ResultRelInfo);	resultRelInfo->ri_RangeTableIndex = 1;		/* dummy */	resultRelInfo->ri_RelationDesc = onerel;	resultRelInfo->ri_TrigDesc = NULL;	/* we don't fire triggers */	ExecOpenIndices(resultRelInfo);	estate->es_result_relations = resultRelInfo;	estate->es_num_result_relations = 1;	estate->es_result_relation_info = resultRelInfo;	/* Set up a dummy tuple table too */	tupleTable = ExecCreateTupleTable(1);	slot = ExecAllocTableSlot(tupleTable);	ExecSetSlotDescriptor(slot, tupdesc, false);	Nvacpagelist.num_pages = 0;	num_fraged_pages = fraged_pages->num_pages;	Assert((BlockNumber) vacuum_pages->num_pages >= vacuum_pages->empty_end_pages);	vacuumed_pages = vacuum_pages->num_pages - vacuum_pages->empty_end_pages;	if (vacuumed_pages > 0)	{		/* get last reaped page from vacuum_pages */		last_vacuum_page = vacuum_pages->pagedesc[vacuumed_pages - 1];		last_vacuum_block = last_vacuum_page->blkno;	}	else	{		last_vacuum_page = NULL;		last_vacuum_block = InvalidBlockNumber;	}	cur_buffer = InvalidBuffer;	num_moved = 0;	vacpage = (VacPage) palloc(sizeof(VacPageData) + MaxOffsetNumber * sizeof(OffsetNumber));	vacpage->offsets_used = vacpage->offsets_free = 0;	/*	 * Scan pages backwards from the last nonempty page, trying to move	 * tuples down to lower pages.	Quit when we reach a page that we have	 * moved any tuples onto, or the first page if we haven't moved	 * anything, or when we find a page we cannot completely empty (this	 * last condition is handled by "break" statements within the loop).	 *	 * NB: this code depends on the vacuum_pages and fraged_pages lists being	 * in order by blkno.	 */	nblocks = vacrelstats->rel_pages;	for (blkno = nblocks - vacuum_pages->empty_end_pages - 1;		 blkno > last_move_dest_block;		 blkno--)	{		CHECK_FOR_INTERRUPTS();		/*		 * Forget fraged_pages pages at or after this one; they're no		 * longer useful as move targets, since we only want to move down.		 * Note that since we stop the outer loop at last_move_dest_block,		 * pages removed here cannot have had anything moved onto them		 * already.		 *		 * Also note that we don't change the stored fraged_pages list, only		 * our local variable num_fraged_pages; so the forgotten pages are		 * still available to be loaded into the free space map later.		 */		while (num_fraged_pages > 0 &&			fraged_pages->pagedesc[num_fraged_pages - 1]->blkno >= blkno)		{			Assert(fraged_pages->pagedesc[num_fraged_pages - 1]->offsets_used == 0);			--num_fraged_pages;		}		/*		 * Process this page of relation.		 */		buf = ReadBuffer(onerel, blkno);		page = BufferGetPage(buf);		vacpage->offsets_free = 0;		isempty = PageIsEmpty(page);		dowrite = false;		/* Is the page in the vacuum_pages list? */		if (blkno == last_vacuum_block)		{			if (last_vacuum_page->offsets_free > 0)			{				/* there are dead tuples on this page - clean them */				Assert(!isempty);				LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);				vacuum_page(onerel, buf, last_vacuum_page);				LockBuffer(buf, BUFFER_LOCK_UNLOCK);				dowrite = true;			}			else				Assert(isempty);			--vacuumed_pages;			if (vacuumed_pages > 0)			{				/* get prev reaped page from vacuum_pages */				last_vacuum_page = vacuum_pages->pagedesc[vacuumed_pages - 1];				last_vacuum_block = last_vacuum_page->blkno;			}			else			{				last_vacuum_page = NULL;				last_vacuum_block = InvalidBlockNumber;			}			if (isempty)			{				ReleaseBuffer(buf);				continue;			}		}		else			Assert(!isempty);		chain_tuple_moved = false;		/* no one chain-tuple was moved										 * off this page, yet */		vacpage->blkno = blkno;		maxoff = PageGetMaxOffsetNumber(page);		for (offnum = FirstOffsetNumber;			 offnum <= maxoff;			 offnum = OffsetNumberNext(offnum))		{			itemid = PageGetItemId(page, offnum);			if (!ItemIdIsUsed(itemid))				continue;			tuple.t_datamcxt = NULL;			tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);			tuple_len = tuple.t_len = ItemIdGetLength(itemid);			ItemPointerSet(&(tuple.t_self), blkno, offnum);			if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))			{				if (tuple.t_data->t_infomask & HEAP_MOVED_IN)					elog(ERROR, "HEAP_MOVED_IN was not expected");				/*				 * If this (chain) tuple is moved by me already then I				 * have to check is it in vacpage or not - i.e. is it				 * moved while cleaning this page or some previous one.				 */				if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)				{					if (HeapTupleHeaderGetXvac(tuple.t_data) != myXID)						elog(ERROR, "invalid XVAC in tuple header");					if (keep_tuples == 0)						continue;					if (chain_tuple_moved)		/* some chains was moved												 * while */					{			/* cleaning this page */						Assert(vacpage->offsets_free > 0);						for (i = 0; i < vacpage->offsets_free; i++)						{							if (vacpage->offsets[i] == offnum)								break;						}						if (i >= vacpage->offsets_free) /* not found */						{							vacpage->offsets[vacpage->offsets_free++] = offnum;							keep_tuples--;						}					}					else					{						vacpage->offsets[vacpage->offsets_free++] = offnum;						keep_tuples--;					}					continue;				}				elog(ERROR, "HEAP_MOVED_OFF was expected");			}			/*			 * If this tuple is in the chain of tuples created in updates			 * by "recent" transactions then we have to move all chain of			 * tuples to another places.			 *			 * NOTE: this test is not 100% accurate: it is possible for a			 * tuple to be an updated one with recent xmin, and yet not			 * have a corresponding tuple in the vtlinks list.	Presumably			 * there was once a parent tuple with xmax matching the xmin,			 * but it's possible that that tuple has been removed --- for			 * example, if it had xmin = xmax then			 * HeapTupleSatisfiesVacuum would deem it removable as soon as			 * the xmin xact completes.			 *			 * To be on the safe side, we abandon the repair_frag process if			 * we cannot find the parent tuple in vtlinks.	This may be			 * overly conservative; AFAICS it would be safe to move the			 * chain.			 */			if (((tuple.t_data->t_infomask & HEAP_UPDATED) &&			 !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),									OldestXmin)) ||				(!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID |											   HEAP_MARKED_FOR_UPDATE)) &&				 !(ItemPointerEquals(&(tuple.t_self),									 &(tuple.t_data->t_ctid)))))			{				Buffer		Cbuf = buf;				bool		freeCbuf = false;				bool		chain_move_failed = false;				Page		Cpage;				ItemId		Citemid;				ItemPointerData Ctid;				HeapTupleData tp = tuple;				Size		tlen = tuple_len;				VTupleMove	vtmove;				int			num_vtmove;				int			free_vtmove;				VacPage		to_vacpage = NULL;				int			to_item = 0;				int			ti;				if (cur_buffer != InvalidBuffer)				{					WriteBuffer(cur_buffer);					cur_buffer = InvalidBuffer;				}				/* Quick exit if we have no vtlinks to search in */				if (vacrelstats->vtlinks == NULL)				{					elog(DEBUG2, "parent item in update-chain not found --- can't continue repair_frag");					break;		/* out of walk-along-page loop */				}				vtmove = (VTupleMove) palloc(100 * sizeof(VTupleMoveData));				num_vtmove = 0;				free_vtmove = 100;				/*				 * If this tuple is in the begin/middle of the chain then				 * we have to move to the end of chain.				 */				while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID |											  HEAP_MARKED_FOR_UPDATE)) &&					   !(ItemPointerEquals(&(tp.t_self),										   &(tp.t_data->t_ctid))))				{					Ctid = tp.t_data->t_ctid;					if (freeCbuf)						ReleaseBuffer(Cbuf);					freeCbuf = true;					Cbuf = ReadBuffer(onerel,									  ItemPointerGetBlockNumber(&Ctid));					Cpage = BufferGetPage(Cbuf);					Citemid = PageGetItemId(Cpage,									  ItemPointerGetOffsetNumber(&Ctid));					if (!ItemIdIsUsed(Citemid))					{						/*						 * This means that in the middle of chain there						 * was tuple updated by older (than OldestXmin)						 * xaction and this tuple is already deleted by						 * me. Actually, upper part of chain should be						 * removed and seems that this should be handled						 * in scan_heap(), but it's not implemented at the						 * moment and so we just stop shrinking here.						 */						elog(DEBUG2, "child itemid in update-chain marked as unused --- can't continue repair_frag");						chain_move_failed = true;						break;	/* out of loop to move to chain end */					}					tp.t_datamcxt = NULL;					tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);					tp.t_self = Ctid;					tlen = tp.t_len = ItemIdGetLength(Citemid);				}				if (chain_move_failed)				{					if (freeCbuf)						ReleaseBuffer(Cbuf);					pfree(vtmove);					break;		/* out of walk-along-page loop */				}				/*				 * Check if all items in chain can be moved				 */				for (;;)				{					Buffer		Pbuf;					Page		Ppage;					ItemId		Pitemid;					HeapTupleData Ptp;					VTupleLinkData vtld,							   *vtlp;					if (to_vacpage == NULL ||						!enough_space(to_vacpage, tlen))					{						for (i = 0; i < num_fraged_pages; i++)						{							if (enough_space(fraged_pages->pagedesc[i], tlen))								break;						}						if (i == num_fraged_pages)						{							/* can't move item anywhere */							chain_move_failed = true;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?