vacuum.c

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

C
2,201
字号
/*------------------------------------------------------------------------- * * vacuum.c *	  The postgres vacuum cleaner. * * This file includes the "full" version of VACUUM, as well as control code * used by all three of full VACUUM, lazy VACUUM, and ANALYZE.	See * vacuumlazy.c and analyze.c for the rest of the code for the latter two. * * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.263 2003/10/02 23:19:44 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <unistd.h>#include "access/clog.h"#include "access/genam.h"#include "access/heapam.h"#include "access/xlog.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/namespace.h"#include "catalog/pg_database.h"#include "catalog/pg_index.h"#include "commands/vacuum.h"#include "executor/executor.h"#include "miscadmin.h"#include "storage/freespace.h"#include "storage/sinval.h"#include "storage/smgr.h"#include "tcop/pquery.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/relcache.h"#include "utils/syscache.h"#include "pgstat.h"typedef struct VacPageData{	BlockNumber blkno;			/* BlockNumber of this Page */	Size		free;			/* FreeSpace on this Page */	uint16		offsets_used;	/* Number of OffNums used by vacuum */	uint16		offsets_free;	/* Number of OffNums free or to be free */	OffsetNumber offsets[1];	/* Array of free OffNums */} VacPageData;typedef VacPageData *VacPage;typedef struct VacPageListData{	BlockNumber empty_end_pages;	/* Number of "empty" end-pages */	int			num_pages;		/* Number of pages in pagedesc */	int			num_allocated_pages;	/* Number of allocated pages in										 * pagedesc */	VacPage    *pagedesc;		/* Descriptions of pages */} VacPageListData;typedef VacPageListData *VacPageList;typedef struct VTupleLinkData{	ItemPointerData new_tid;	ItemPointerData this_tid;} VTupleLinkData;typedef VTupleLinkData *VTupleLink;typedef struct VTupleMoveData{	ItemPointerData tid;		/* tuple ID */	VacPage		vacpage;		/* where to move */	bool		cleanVpd;		/* clean vacpage before using */} VTupleMoveData;typedef VTupleMoveData *VTupleMove;typedef struct VRelStats{	BlockNumber rel_pages;	double		rel_tuples;	Size		min_tlen;	Size		max_tlen;	bool		hasindex;	int			num_vtlinks;	VTupleLink	vtlinks;} VRelStats;static MemoryContext vac_context = NULL;static int	elevel = -1;static TransactionId OldestXmin;static TransactionId FreezeLimit;/* non-export function prototypes */static List *getrels(const RangeVar *vacrel, const char *stmttype);static void vac_update_dbstats(Oid dbid,				   TransactionId vacuumXID,				   TransactionId frozenXID);static void vac_truncate_clog(TransactionId vacuumXID,				  TransactionId frozenXID);static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind);static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);static void scan_heap(VRelStats *vacrelstats, Relation onerel,		  VacPageList vacuum_pages, VacPageList fraged_pages);static void repair_frag(VRelStats *vacrelstats, Relation onerel,			VacPageList vacuum_pages, VacPageList fraged_pages,			int nindexes, Relation *Irel);static void vacuum_heap(VRelStats *vacrelstats, Relation onerel,			VacPageList vacpagelist);static void vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage);static void vacuum_index(VacPageList vacpagelist, Relation indrel,			 double num_tuples, int keep_tuples);static void scan_index(Relation indrel, double num_tuples);static bool tid_reaped(ItemPointer itemptr, void *state);static bool dummy_tid_reaped(ItemPointer itemptr, void *state);static void vac_update_fsm(Relation onerel, VacPageList fraged_pages,			   BlockNumber rel_pages);static VacPage copy_vac_page(VacPage vacpage);static void vpage_insert(VacPageList vacpagelist, VacPage vpnew);static void *vac_bsearch(const void *key, const void *base,			size_t nelem, size_t size,			int (*compar) (const void *, const void *));static int	vac_cmp_blk(const void *left, const void *right);static int	vac_cmp_offno(const void *left, const void *right);static int	vac_cmp_vtlinks(const void *left, const void *right);static bool enough_space(VacPage vacpage, Size len);/**************************************************************************** *																			* *			Code common to all flavors of VACUUM and ANALYZE				* *																			* **************************************************************************** *//* * Primary entry point for VACUUM and ANALYZE commands. */voidvacuum(VacuumStmt *vacstmt){	const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";	MemoryContext anl_context = NULL;	TransactionId initialOldestXmin = InvalidTransactionId;	TransactionId initialFreezeLimit = InvalidTransactionId;	bool		all_rels;	List	   *vrl,			   *cur;	if (vacstmt->verbose)		elevel = INFO;	else		elevel = DEBUG2;	/*	 * We cannot run VACUUM inside a user transaction block; if we were	 * inside a transaction, then our commit- and	 * start-transaction-command calls would not have the intended effect!	 * Furthermore, the forced commit that occurs before truncating the	 * relation's file would have the effect of committing the rest of the	 * user's transaction too, which would certainly not be the desired	 * behavior.	 */	if (vacstmt->vacuum)		PreventTransactionChain((void *) vacstmt, stmttype);	/*	 * Send info about dead objects to the statistics collector	 */	if (vacstmt->vacuum)		pgstat_vacuum_tabstat();	/*	 * Create special memory context for cross-transaction storage.	 *	 * Since it is a child of PortalContext, it will go away eventually even	 * if we suffer an error; there's no need for special abort cleanup	 * logic.	 */	vac_context = AllocSetContextCreate(PortalContext,										"Vacuum",										ALLOCSET_DEFAULT_MINSIZE,										ALLOCSET_DEFAULT_INITSIZE,										ALLOCSET_DEFAULT_MAXSIZE);	/*	 * If we are running only ANALYZE, we don't need per-table	 * transactions, but we still need a memory context with table	 * lifetime.	 */	if (vacstmt->analyze && !vacstmt->vacuum)		anl_context = AllocSetContextCreate(PortalContext,											"Analyze",											ALLOCSET_DEFAULT_MINSIZE,											ALLOCSET_DEFAULT_INITSIZE,											ALLOCSET_DEFAULT_MAXSIZE);	/* Assume we are processing everything unless one table is mentioned */	all_rels = (vacstmt->relation == NULL);	/* Build list of relations to process (note this lives in vac_context) */	vrl = getrels(vacstmt->relation, stmttype);	/*	 * Formerly, there was code here to prevent more than one VACUUM from	 * executing concurrently in the same database.  However, there's no	 * good reason to prevent that, and manually removing lockfiles after	 * a vacuum crash was a pain for dbadmins.	So, forget about	 * lockfiles, and just rely on the locks we grab on each target table	 * to ensure that there aren't two VACUUMs running on the same table	 * at the same time.	 */	/*	 * The strangeness with committing and starting transactions here is	 * due to wanting to run each table's VACUUM as a separate	 * transaction, so that we don't hold locks unnecessarily long.  Also,	 * if we are doing VACUUM ANALYZE, the ANALYZE part runs as a separate	 * transaction from the VACUUM to further reduce locking.	 *	 * vacuum_rel expects to be entered with no transaction active; it will	 * start and commit its own transaction.  But we are called by an SQL	 * command, and so we are executing inside a transaction already.  We	 * commit the transaction started in PostgresMain() here, and start	 * another one before exiting to match the commit waiting for us back	 * in PostgresMain().	 *	 * In the case of an ANALYZE statement (no vacuum, just analyze) it's	 * okay to run the whole thing in the outer transaction, and so we	 * skip transaction start/stop operations.	 */	if (vacstmt->vacuum)	{		if (all_rels)		{			/*			 * It's a database-wide VACUUM.			 *			 * Compute the initially applicable OldestXmin and FreezeLimit			 * XIDs, so that we can record these values at the end of the			 * VACUUM. Note that individual tables may well be processed			 * with newer values, but we can guarantee that no			 * (non-shared) relations are processed with older ones.			 *			 * It is okay to record non-shared values in pg_database, even			 * though we may vacuum shared relations with older cutoffs,			 * because only the minimum of the values present in			 * pg_database matters.  We can be sure that shared relations			 * have at some time been vacuumed with cutoffs no worse than			 * the global minimum; for, if there is a backend in some			 * other DB with xmin = OLDXMIN that's determining the cutoff			 * with which we vacuum shared relations, it is not possible			 * for that database to have a cutoff newer than OLDXMIN			 * recorded in pg_database.			 */			vacuum_set_xid_limits(vacstmt, false,								  &initialOldestXmin,								  &initialFreezeLimit);		}		/* matches the StartTransaction in PostgresMain() */		CommitTransactionCommand();	}	/*	 * Loop to process each selected relation.	 */	foreach(cur, vrl)	{		Oid			relid = lfirsto(cur);		if (vacstmt->vacuum)		{			if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))				all_rels = false;		/* forget about updating dbstats */		}		if (vacstmt->analyze)		{			MemoryContext old_context = NULL;			/*			 * If we vacuumed, use new transaction for analyze. Otherwise,			 * we can use the outer transaction, but we still need to call			 * analyze_rel in a memory context that will be cleaned up on			 * return (else we leak memory while processing multiple			 * tables).			 */			if (vacstmt->vacuum)			{				StartTransactionCommand();				SetQuerySnapshot();		/* might be needed for functions										 * in indexes */			}			else				old_context = MemoryContextSwitchTo(anl_context);			analyze_rel(relid, vacstmt);			if (vacstmt->vacuum)				CommitTransactionCommand();			else			{				MemoryContextSwitchTo(old_context);				MemoryContextResetAndDeleteChildren(anl_context);			}		}	}	/*	 * Finish up processing.	 */	if (vacstmt->vacuum)	{		/* here, we are not in a transaction */		/*		 * This matches the CommitTransaction waiting for us in		 * PostgresMain().		 */		StartTransactionCommand();		/*		 * If it was a database-wide VACUUM, print FSM usage statistics		 * (we don't make you be superuser to see these).		 */		if (vacstmt->relation == NULL)			PrintFreeSpaceMapStatistics(elevel);		/*		 * If we completed a database-wide VACUUM without skipping any		 * relations, update the database's pg_database row with info		 * about the transaction IDs used, and try to truncate pg_clog.		 */		if (all_rels)		{			vac_update_dbstats(MyDatabaseId,							   initialOldestXmin, initialFreezeLimit);			vac_truncate_clog(initialOldestXmin, initialFreezeLimit);		}	}	/*	 * Clean up working storage --- note we must do this after	 * StartTransactionCommand, else we might be trying to delete the	 * active context!	 */	MemoryContextDelete(vac_context);	vac_context = NULL;	if (anl_context)		MemoryContextDelete(anl_context);}/* * Build a list of Oids for each relation to be processed * * The list is built in vac_context so that it will survive across our * per-relation transactions. */static List *getrels(const RangeVar *vacrel, const char *stmttype){	List	   *vrl = NIL;	MemoryContext oldcontext;	if (vacrel)	{		/* Process specific relation */		Oid			relid;		relid = RangeVarGetRelid(vacrel, false);		/* Make a relation list entry for this guy */		oldcontext = MemoryContextSwitchTo(vac_context);		vrl = lappendo(vrl, relid);		MemoryContextSwitchTo(oldcontext);	}	else	{		/* Process all plain relations listed in pg_class */		Relation	pgclass;		HeapScanDesc scan;		HeapTuple	tuple;		ScanKeyData key;		ScanKeyEntryInitialize(&key, 0x0,							   Anum_pg_class_relkind,							   F_CHAREQ,							   CharGetDatum(RELKIND_RELATION));		pgclass = heap_openr(RelationRelationName, AccessShareLock);		scan = heap_beginscan(pgclass, SnapshotNow, 1, &key);		while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)		{			/* Make a relation list entry for this guy */			oldcontext = MemoryContextSwitchTo(vac_context);			vrl = lappendo(vrl, HeapTupleGetOid(tuple));			MemoryContextSwitchTo(oldcontext);		}		heap_endscan(scan);		heap_close(pgclass, AccessShareLock);	}	return vrl;}/* * vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points */voidvacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,					  TransactionId *oldestXmin,					  TransactionId *freezeLimit){	TransactionId limit;	*oldestXmin = GetOldestXmin(sharedRel);	Assert(TransactionIdIsNormal(*oldestXmin));	if (vacstmt->freeze)	{

⌨️ 快捷键说明

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