📄 vacuum.c
字号:
*/ if (use_own_xacts) { /* matches the StartTransaction in PostgresMain() */ CommitTransactionCommand(); } /* Turn vacuum cost accounting on or off */ PG_TRY(); { ListCell *cur; VacuumCostActive = (VacuumCostDelay > 0); VacuumCostBalance = 0; /* * Loop to process each selected relation. */ foreach(cur, relations) { Oid relid = lfirst_oid(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 using separate xacts, start one 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 (use_own_xacts) { StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } else old_context = MemoryContextSwitchTo(anl_context); /* * Tell the buffer replacement strategy that vacuum is causing * the IO */ StrategyHintVacuum(true); analyze_rel(relid, vacstmt); StrategyHintVacuum(false); if (use_own_xacts) CommitTransactionCommand(); else { MemoryContextSwitchTo(old_context); MemoryContextResetAndDeleteChildren(anl_context); } } } } PG_CATCH(); { /* Make sure cost accounting is turned off after error */ VacuumCostActive = false; PG_RE_THROW(); } PG_END_TRY(); /* Turn off vacuum cost accounting */ VacuumCostActive = false; /* * Finish up processing. */ if (use_own_xacts) { /* here, we are not in a transaction */ /* * This matches the CommitTransaction waiting for us in * PostgresMain(). */ StartTransactionCommand(); /* * Re-establish the transaction snapshot. This is wasted effort * when we are called as a normal utility command, because the * new transaction will be dropped immediately by PostgresMain(); * but it's necessary if we are called from autovacuum because * autovacuum might continue on to do an ANALYZE-only call. */ ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } if (vacstmt->vacuum) { /* * If it was a database-wide VACUUM, print FSM usage statistics (we * don't make you be superuser to see these). */ if (all_rels) 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 *get_rel_oids(List *relids, const RangeVar *vacrel, const char *stmttype){ List *oid_list = NIL; MemoryContext oldcontext; /* List supplied by VACUUM's caller? */ if (relids) return relids; if (vacrel) { /* Process a specific relation */ Oid relid; relid = RangeVarGetRelid(vacrel, false); /* Make a relation list entry for this guy */ oldcontext = MemoryContextSwitchTo(vac_context); oid_list = lappend_oid(oid_list, relid); MemoryContextSwitchTo(oldcontext); } else { /* Process all plain relations listed in pg_class */ Relation pgclass; HeapScanDesc scan; HeapTuple tuple; ScanKeyData key; ScanKeyInit(&key, Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION)); pgclass = heap_open(RelationRelationId, 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); oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple)); MemoryContextSwitchTo(oldcontext); } heap_endscan(scan); heap_close(pgclass, AccessShareLock); } return oid_list;}/* * 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) { /* FREEZE option: use oldest Xmin as freeze cutoff too */ limit = *oldestXmin; } else { /* * Normal case: freeze cutoff is well in the past, to wit, about * halfway to the wrap horizon */ limit = GetCurrentTransactionId() - (MaxTransactionId >> 2); } /* * Be careful not to generate a "permanent" XID */ if (!TransactionIdIsNormal(limit)) limit = FirstNormalTransactionId; /* * Ensure sane relationship of limits */ if (TransactionIdFollows(limit, *oldestXmin)) { ereport(WARNING, (errmsg("oldest xmin is far in the past"), errhint("Close open transactions soon to avoid wraparound problems."))); limit = *oldestXmin; } *freezeLimit = limit;}/* * vac_update_relstats() -- update statistics for one relation * * Update the whole-relation statistics that are kept in its pg_class * row. There are additional stats that will be updated if we are * doing ANALYZE, but we always update these stats. This routine works * for both index and heap relation entries in pg_class. * * We violate no-overwrite semantics here by storing new values for the * statistics columns directly into the pg_class tuple that's already on * the page. The reason for this is that if we updated these tuples in * the usual way, vacuuming pg_class itself wouldn't work very well --- * by the time we got done with a vacuum cycle, most of the tuples in * pg_class would've been obsoleted. Of course, this only works for * fixed-size never-null columns, but these are. * * This routine is shared by full VACUUM, lazy VACUUM, and stand-alone * ANALYZE. */voidvac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, bool hasindex){ Relation rd; HeapTupleData rtup; HeapTuple ctup; Form_pg_class pgcform; Buffer buffer; /* * update number of tuples and number of pages in pg_class */ rd = heap_open(RelationRelationId, RowExclusiveLock); ctup = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); if (!HeapTupleIsValid(ctup)) elog(ERROR, "pg_class entry for relid %u vanished during vacuuming", relid); /* get the buffer cache tuple */ rtup.t_self = ctup->t_self; ReleaseSysCache(ctup); if (!heap_fetch(rd, SnapshotNow, &rtup, &buffer, false, NULL)) elog(ERROR, "pg_class entry for relid %u vanished during vacuuming", relid); /* ensure no one else does this at the same time */ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* overwrite the existing statistics in the tuple */ pgcform = (Form_pg_class) GETSTRUCT(&rtup); pgcform->relpages = (int32) num_pages; pgcform->reltuples = (float4) num_tuples; pgcform->relhasindex = hasindex; /* * If we have discovered that there are no indexes, then there's no * primary key either. This could be done more thoroughly... */ if (!hasindex) pgcform->relhaspkey = false; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* * Invalidate the tuple in the catcaches; this also arranges to flush the * relation's relcache entry. (If we fail to commit for some reason, no * flush will occur, but no great harm is done since there are no * noncritical state updates here.) */ CacheInvalidateHeapTuple(rd, &rtup); /* Write the buffer */ WriteBuffer(buffer); heap_close(rd, RowExclusiveLock);}/* * vac_update_dbstats() -- update statistics for one database * * Update the whole-database statistics that are kept in its pg_database * row, and the flat-file copy of pg_database. * * We violate no-overwrite semantics here by storing new values for the * statistics columns directly into the tuple that's already on the page. * As with vac_update_relstats, this avoids leaving dead tuples behind * after a VACUUM. * * This routine is shared by full and lazy VACUUM. Note that it is only * applied after a database-wide VACUUM operation. */static voidvac_update_dbstats(Oid dbid, TransactionId vacuumXID, TransactionId frozenXID){ Relation relation; ScanKeyData entry[1]; HeapScanDesc scan; HeapTuple tuple; Form_pg_database dbform; relation = heap_open(DatabaseRelationId, RowExclusiveLock); /* Must use a heap scan, since there's no syscache for pg_database */ ScanKeyInit(&entry[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(dbid)); scan = heap_beginscan(relation, SnapshotNow, 1, entry); tuple = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for database %u", dbid); /* ensure no one else does this at the same time */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); dbform = (Form_pg_database) GETSTRUCT(tuple); /* overwrite the existing statistics in the tuple */ dbform->datvacuumxid = vacuumXID; dbform->datfrozenxid = frozenXID; LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* invalidate the tuple in the cache and write the buffer */ CacheInvalidateHeapTuple(relation, tuple); WriteNoReleaseBuffer(scan->rs_cbuf); heap_endscan(scan); heap_close(relation, RowExclusiveLock); /* Mark the flat-file copy of pg_database for update at commit */ database_file_update_needed();}/* * vac_truncate_clog() -- attempt to truncate the commit log * * Scan pg_database to determine the system-wide oldest datvacuumxid, * and use it to truncate the transaction commit log (pg_clog). * Also update the XID wrap limit point maintained by varsup.c. * * We also generate a warning if the system-wide oldest datfrozenxid * seems to be in danger of wrapping around. This is a long-in-advance * warning; if we start getting uncomfortably close, GetNewTransactionId * will generate more-annoying warnings, and ultimately refuse to issue * any more new XIDs. * * The passed XIDs are simply the ones I just wrote into my pg_database * entry. They're used to initialize the "min" calculations. * * This routine is shared by full and lazy VACUUM. Note that it is only * applied after a database-wide VACUUM operation. */static voidvac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID){ TransactionId myXID = GetCurrentTransactionId(); Relation relation; HeapScanDesc scan; HeapTuple tuple; int32 age; NameData oldest_datname; bool vacuumAlreadyWrapped = false; bool frozenAlreadyWrapped = false; /* init oldest_datname to sync with my frozenXID */ namestrcpy(&oldest_datname, get_database_name(MyDatabaseId)); /* * Note: the "already wrapped" cases should now be impossible due to the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -