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 + -
显示快捷键?