提交 d4cef0aa 编写于 作者: A Alvaro Herrera

Improve vacuum code to track minimum Xids per table instead of per database.

To this end, add a couple of columns to pg_class, relminxid and relvacuumxid,
based on which we calculate the pg_database columns after each vacuum.

We now force all databases to be vacuumed, even template ones.  A backend
noticing too old a database (meaning pg_database.datminxid is in danger of
falling behind Xid wraparound) will signal the postmaster, which in turn will
start an autovacuum iteration to process the offending database.  In principle
this is only there to cope with frozen (non-connectable) databases without
forcing users to set them to connectable, but it could force regular user
database to go through a database-wide vacuum at any time.  Maybe we should
warn users about this somehow.  Of course the real solution will be to use
autovacuum all the time ;-)

There are some additional improvements we could have in this area: for example
the vacuum code could be smarter about not updating pg_database for each table
when called by autovacuum, and do it only once the whole autovacuum iteration
is done.

I updated the system catalogs documentation, but I didn't modify the
maintenance section.  Also having some regression tests for this would be nice
but it's not really a very straightforward thing to do.

Catalog version bumped due to system catalog changes.
上级 e627c9b9
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.125 2006/07/03 22:45:36 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.126 2006/07/10 16:20:49 alvherre Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
......@@ -1639,6 +1639,30 @@
<entry>True if table has (or once had) any inheritance children</entry>
</row>
<row>
<entry><structfield>relminxid</structfield></entry>
<entry><type>xid</type></entry>
<entry></entry>
<entry>
The minimum transaction ID present in all rows in this table. This
value is used to determine the database-global
<structname>pg_database</>.<structfield>datminxid</> value.
</entry>
</row>
<row>
<entry><structfield>relvacuumxid</structfield></entry>
<entry><type>xid</type></entry>
<entry></entry>
<entry>
The transaction ID that was used as cleaning point as of the last vacuum
operation. All rows inserted, updated or deleted in this table by
transactions whose IDs are below this one have been marked as known good
or deleted. This is used to determine the database-global
<structname>pg_database</>.<structfield>datvacuumxid</> value.
</entry>
</row>
<row>
<entry><structfield>relacl</structfield></entry>
<entry><type>aclitem[]</type></entry>
......@@ -2022,21 +2046,27 @@
<entry><type>xid</type></entry>
<entry></entry>
<entry>
All rows inserted or deleted by transaction IDs before this one
have been marked as known committed or known aborted in this database.
This is used to determine when commit-log space can be recycled.
The transaction ID that was used as cleaning point as of the last vacuum
operation. All rows inserted or deleted by transaction IDs before this one
have been marked as known good or deleted. This
is used to determine when commit-log space can be recycled.
If InvalidTransactionId, then the minimum is unknown and can be
determined by scanning <structname>pg_class</>.<structfield>relvacuumxid</>.
</entry>
</row>
<row>
<entry><structfield>datfrozenxid</structfield></entry>
<entry><structfield>datminxid</structfield></entry>
<entry><type>xid</type></entry>
<entry></entry>
<entry>
The minimum transaction ID present in all tables in this database.
All rows inserted by transaction IDs before this one have been
relabeled with a permanent (<quote>frozen</>) transaction ID in this
database. This is useful to check whether a database must be vacuumed
soon to avoid transaction ID wrap-around problems.
database. This is useful to check whether a database must be
vacuumed soon to avoid transaction ID wrap-around problems.
If InvalidTransactionId, then the minimum is unknown and can be
determined by scanning <structname>pg_class</>.<structfield>relminxid</>.
</entry>
</row>
......
......@@ -6,7 +6,7 @@
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.70 2006/03/05 15:58:22 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.71 2006/07/10 16:20:49 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -168,11 +168,11 @@ ReadNewTransactionId(void)
/*
* Determine the last safe XID to allocate given the currently oldest
* datfrozenxid (ie, the oldest XID that might exist in any database
* datminxid (ie, the oldest XID that might exist in any database
* of our cluster).
*/
void
SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
SetTransactionIdLimit(TransactionId oldest_datminxid,
Name oldest_datname)
{
TransactionId xidWarnLimit;
......@@ -180,16 +180,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
TransactionId xidWrapLimit;
TransactionId curXid;
Assert(TransactionIdIsValid(oldest_datfrozenxid));
Assert(TransactionIdIsValid(oldest_datminxid));
/*
* The place where we actually get into deep trouble is halfway around
* from the oldest potentially-existing XID. (This calculation is
* probably off by one or two counts, because the special XIDs reduce the
* size of the loop a little bit. But we throw in plenty of slop below,
* so it doesn't matter.)
* from the oldest existing XID. (This calculation is probably off by one
* or two counts, because the special XIDs reduce the size of the loop a
* little bit. But we throw in plenty of slop below, so it doesn't
* matter.)
*/
xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
xidWrapLimit = oldest_datminxid + (MaxTransactionId >> 1);
if (xidWrapLimit < FirstNormalTransactionId)
xidWrapLimit += FirstNormalTransactionId;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.305 2006/07/08 20:45:38 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.306 2006/07/10 16:20:49 alvherre Exp $
*
*
* INTERFACE ROUTINES
......@@ -597,6 +597,8 @@ InsertPgClassTuple(Relation pg_class_desc,
values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relminxid - 1] = TransactionIdGetDatum(rd_rel->relminxid);
values[Anum_pg_class_relvacuumxid - 1] = TransactionIdGetDatum(rd_rel->relvacuumxid);
/* start out with empty permissions */
nulls[Anum_pg_class_relacl - 1] = 'n';
if (reloptions != (Datum) 0)
......@@ -644,6 +646,35 @@ AddNewRelationTuple(Relation pg_class_desc,
*/
new_rel_reltup = new_rel_desc->rd_rel;
/* Initialize relminxid and relvacuumxid */
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_TOASTVALUE)
{
/*
* Only real tables have Xids stored in them; initialize our known
* value to the minimum Xid that could put tuples in the new table.
*/
if (!IsBootstrapProcessingMode())
{
new_rel_reltup->relminxid = RecentXmin;
new_rel_reltup->relvacuumxid = RecentXmin;
}
else
{
new_rel_reltup->relminxid = FirstNormalTransactionId;
new_rel_reltup->relvacuumxid = FirstNormalTransactionId;
}
}
else
{
/*
* Other relations will not have Xids in them, so set the initial value
* to InvalidTransactionId.
*/
new_rel_reltup->relminxid = InvalidTransactionId;
new_rel_reltup->relvacuumxid = InvalidTransactionId;
}
switch (relkind)
{
case RELKIND_RELATION:
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.93 2006/03/23 00:19:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.94 2006/07/10 16:20:50 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -424,8 +424,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
{
vac_update_relstats(RelationGetRelid(onerel),
RelationGetNumberOfBlocks(onerel),
totalrows,
hasindex);
totalrows, hasindex,
InvalidTransactionId, InvalidTransactionId);
for (ind = 0; ind < nindexes; ind++)
{
AnlIndexData *thisdata = &indexdata[ind];
......@@ -434,8 +435,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
totalindexrows = ceil(thisdata->tupleFract * totalrows);
vac_update_relstats(RelationGetRelid(Irel[ind]),
RelationGetNumberOfBlocks(Irel[ind]),
totalindexrows,
false);
totalindexrows, false,
InvalidTransactionId, InvalidTransactionId);
}
/* report results to the stats collector, too */
......
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.181 2006/05/04 16:07:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.182 2006/07/10 16:20:50 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -55,7 +55,7 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
Oid *dbTablespace);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id);
......@@ -76,7 +76,7 @@ createdb(const CreatedbStmt *stmt)
bool src_allowconn;
Oid src_lastsysoid;
TransactionId src_vacuumxid;
TransactionId src_frozenxid;
TransactionId src_minxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
Relation pg_database_rel;
......@@ -228,7 +228,7 @@ createdb(const CreatedbStmt *stmt)
if (!get_db_info(dbtemplate, ShareLock,
&src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_allowconn, &src_lastsysoid,
&src_vacuumxid, &src_frozenxid, &src_deftablespace))
&src_vacuumxid, &src_minxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("template database \"%s\" does not exist",
......@@ -326,16 +326,6 @@ createdb(const CreatedbStmt *stmt)
/* Note there is no additional permission check in this path */
}
/*
* Normally we mark the new database with the same datvacuumxid and
* datfrozenxid as the source. However, if the source is not allowing
* connections then we assume it is fully frozen, and we can set the
* current transaction ID as the xid limits. This avoids immediately
* starting to generate warnings after cloning template0.
*/
if (!src_allowconn)
src_vacuumxid = src_frozenxid = GetCurrentTransactionId();
/*
* Check for db name conflict. This is just to give a more friendly
* error message than "unique index violation". There's a race condition
......@@ -367,7 +357,7 @@ createdb(const CreatedbStmt *stmt)
new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
new_record[Anum_pg_database_datminxid - 1] = TransactionIdGetDatum(src_minxid);
new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
/*
......@@ -1066,7 +1056,7 @@ get_db_info(const char *name, LOCKMODE lockmode,
Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
Oid *dbTablespace)
{
bool result = false;
......@@ -1155,9 +1145,9 @@ get_db_info(const char *name, LOCKMODE lockmode,
/* limit of vacuumed XIDs */
if (dbVacuumXidP)
*dbVacuumXidP = dbform->datvacuumxid;
/* limit of frozen XIDs */
if (dbFrozenXidP)
*dbFrozenXidP = dbform->datfrozenxid;
/* limit of min XIDs */
if (dbMinXidP)
*dbMinXidP = dbform->datminxid;
/* default tablespace for this database */
if (dbTablespace)
*dbTablespace = dbform->dattablespace;
......
此差异已折叠。
......@@ -31,7 +31,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.72 2006/07/03 22:45:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.73 2006/07/10 16:20:50 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -42,6 +42,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "pgstat.h"
......@@ -72,6 +73,7 @@ typedef struct LVRelStats
double tuples_deleted;
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
Size threshold; /* minimum interesting free space */
TransactionId minxid; /* minimum Xid present anywhere in table */
/* List of TIDs of tuples we intend to delete */
/* NB: this list is ordered by TID address */
int num_dead_tuples; /* current # of entries */
......@@ -88,13 +90,11 @@ typedef struct LVRelStats
static int elevel = -1;
static TransactionId OldestXmin;
static TransactionId FreezeLimit;
/* non-export function prototypes */
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, int nindexes);
Relation *Irel, int nindexes, TransactionId FreezeLimit,
TransactionId OldestXmin);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats,
......@@ -104,9 +104,10 @@ static void lazy_cleanup_index(Relation indrel,
LVRelStats *vacrelstats);
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats);
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
TransactionId OldestXmin);
static BlockNumber count_nondeletable_pages(Relation onerel,
LVRelStats *vacrelstats);
LVRelStats *vacrelstats, TransactionId OldestXmin);
static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
ItemPointer itemptr);
......@@ -122,7 +123,8 @@ static int vac_cmp_page_spaces(const void *left, const void *right);
* lazy_vacuum_rel() -- perform LAZY VACUUM for one heap relation
*
* This routine vacuums a single heap, cleans out its indexes, and
* updates its num_pages and num_tuples statistics.
* updates its relpages and reltuples statistics, as well as the
* relminxid and relvacuumxid information.
*
* At entry, we have already established a transaction and opened
* and locked the relation.
......@@ -135,6 +137,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
int nindexes;
bool hasindex;
BlockNumber possibly_freeable;
TransactionId OldestXmin,
FreezeLimit;
if (vacstmt->verbose)
elevel = INFO;
......@@ -150,12 +154,23 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
/*
* Set initial minimum Xid, which will be updated if a smaller Xid is found
* in the relation by lazy_scan_heap.
*
* We use RecentXmin here (the minimum Xid that belongs to a transaction
* that is still open according to our snapshot), because it is the
* earliest transaction that could concurrently insert new tuples in the
* table.
*/
vacrelstats->minxid = RecentXmin;
/* Open all indexes of the relation */
vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel);
hasindex = (nindexes > 0);
/* Do the vacuuming */
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes);
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, FreezeLimit, OldestXmin);
/* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock);
......@@ -169,7 +184,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
if (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
lazy_truncate_heap(onerel, vacrelstats);
lazy_truncate_heap(onerel, vacrelstats, OldestXmin);
/* Update shared free space map with final free space info */
lazy_update_fsm(onerel, vacrelstats);
......@@ -178,7 +193,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
vac_update_relstats(RelationGetRelid(onerel),
vacrelstats->rel_pages,
vacrelstats->rel_tuples,
hasindex);
hasindex,
vacrelstats->minxid, OldestXmin);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
......@@ -193,10 +209,14 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
* and pages with free space, and calculates statistics on the number
* of live tuples in the heap. When done, or when we run low on space
* for dead-tuple TIDs, invoke vacuuming of indexes and heap.
*
* It also updates the minimum Xid found anywhere on the table in
* vacrelstats->minxid, for later storing it in pg_class.relminxid.
*/
static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, int nindexes)
Relation *Irel, int nindexes, TransactionId FreezeLimit,
TransactionId OldestXmin)
{
BlockNumber nblocks,
blkno;
......@@ -406,8 +426,19 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
}
else
{
TransactionId min;
num_tuples += 1;
hastup = true;
/*
* If the tuple is alive, we consider it for the "minxid"
* calculations.
*/
min = vactuple_get_minxid(&tuple);
if (TransactionIdIsValid(min) &&
TransactionIdPrecedes(min, vacrelstats->minxid))
vacrelstats->minxid = min;
}
} /* scan along page */
......@@ -670,7 +701,7 @@ lazy_cleanup_index(Relation indrel,
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
false);
false, InvalidTransactionId, InvalidTransactionId);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
......@@ -691,7 +722,8 @@ lazy_cleanup_index(Relation indrel,
* lazy_truncate_heap - try to truncate off any empty pages at the end
*/
static void
lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
TransactionId OldestXmin)
{
BlockNumber old_rel_pages = vacrelstats->rel_pages;
BlockNumber new_rel_pages;
......@@ -732,7 +764,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
* because other backends could have added tuples to these pages whilst we
* were vacuuming.
*/
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats);
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats, OldestXmin);
if (new_rel_pages >= old_rel_pages)
{
......@@ -787,7 +819,8 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
* Returns number of nondeletable pages (last nonempty page + 1).
*/
static BlockNumber
count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats,
TransactionId OldestXmin)
{
BlockNumber blkno;
HeapTupleData tuple;
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.152 2006/06/20 19:56:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.153 2006/07/10 16:20:50 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1004,7 +1004,7 @@ load_hba(void)
* dbname: gets database name (must be of size NAMEDATALEN bytes)
* dboid: gets database OID
* dbtablespace: gets database's default tablespace's OID
* dbfrozenxid: gets database's frozen XID
* dbminxid: gets database's minimum XID
* dbvacuumxid: gets database's vacuum XID
*
* This is not much related to the other functions in hba.c, but we put it
......@@ -1012,7 +1012,7 @@ load_hba(void)
*/
bool
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
Oid *dbtablespace, TransactionId *dbfrozenxid,
Oid *dbtablespace, TransactionId *dbminxid,
TransactionId *dbvacuumxid)
{
char buf[MAX_TOKEN];
......@@ -1035,7 +1035,7 @@ read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
*dbfrozenxid = atoxid(buf);
*dbminxid = atoxid(buf);
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.21 2006/06/27 22:16:43 momjian Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.22 2006/07/10 16:20:50 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -79,7 +79,7 @@ typedef struct autovac_dbase
{
Oid oid;
char *name;
TransactionId frozenxid;
TransactionId minxid;
TransactionId vacuumxid;
PgStat_StatDBEntry *entry;
int32 age;
......@@ -126,10 +126,6 @@ autovac_start(void)
time_t curtime;
pid_t AutoVacPID;
/* Do nothing if no autovacuum process needed */
if (!AutoVacuumingActive())
return 0;
/*
* Do nothing if too soon since last autovacuum exit. This limits how
* often the daemon runs. Since the time per iteration can be quite
......@@ -144,6 +140,7 @@ autovac_start(void)
*
* XXX todo: implement sleep scale factor that existed in contrib code.
*/
curtime = time(NULL);
if ((unsigned int) (curtime - last_autovac_stop_time) <
(unsigned int) autovacuum_naptime)
......@@ -334,6 +331,14 @@ AutoVacMain(int argc, char *argv[])
* connected to it since the stats were last initialized, it doesn't need
* vacuuming.
*
* Note that if we are called when autovacuum is nominally disabled in
* postgresql.conf, we assume the postmaster has invoked us because a
* database is in danger of Xid wraparound. In that case, we only
* consider vacuuming whole databases, not individual tables; and we pick
* the oldest one, regardless of it's true age. So the criteria for
* deciding that a database needs a database-wide vacuum is elsewhere
* (currently in vac_truncate_clog).
*
* XXX This could be improved if we had more info about whether it needs
* vacuuming before connecting to it. Perhaps look through the pgstats
* data for the database's tables? One idea is to keep track of the
......@@ -344,13 +349,8 @@ AutoVacMain(int argc, char *argv[])
db = NULL;
whole_db = false;
foreach(cell, dblist)
if (AutoVacuumingActive())
{
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
int32 freeze_age,
vacuum_age;
/*
* We look for the database that most urgently needs a database-wide
* vacuum. We decide that a database-wide vacuum is needed 100000
......@@ -361,38 +361,70 @@ AutoVacMain(int argc, char *argv[])
* Unlike vacuum.c, we also look at vacuumxid. This is so that
* pg_clog can be kept trimmed to a reasonable size.
*/
freeze_age = (int32) (nextXid - tmp->frozenxid);
vacuum_age = (int32) (nextXid - tmp->vacuumxid);
tmp->age = Max(freeze_age, vacuum_age);
this_whole_db = (tmp->age >
(int32) ((MaxTransactionId >> 3) * 3 - 100000));
if (whole_db || this_whole_db)
foreach(cell, dblist)
{
if (!this_whole_db)
continue;
if (db == NULL || tmp->age > db->age)
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
int32 true_age,
vacuum_age;
true_age = (int32) (nextXid - tmp->minxid);
vacuum_age = (int32) (nextXid - tmp->vacuumxid);
tmp->age = Max(true_age, vacuum_age);
this_whole_db = (tmp->age >
(int32) ((MaxTransactionId >> 3) * 3 - 100000));
if (whole_db || this_whole_db)
{
db = tmp;
whole_db = true;
if (!this_whole_db)
continue;
if (db == NULL || tmp->age > db->age)
{
db = tmp;
whole_db = true;
}
continue;
}
continue;
}
/*
* Otherwise, skip a database with no pgstat entry; it means it hasn't
* seen any activity.
*/
tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);
if (!tmp->entry)
continue;
/*
* Otherwise, skip a database with no pgstat entry; it means it hasn't
* seen any activity.
*/
tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);
if (!tmp->entry)
continue;
/*
* Remember the db with oldest autovac time.
*/
if (db == NULL ||
tmp->entry->last_autovac_time < db->entry->last_autovac_time)
db = tmp;
}
}
else
{
/*
* Remember the db with oldest autovac time.
* If autovacuuming is not active, we must have gotten here because a
* backend signalled the postmaster. Pick up the database with the
* greatest age, and apply a database-wide vacuum on it.
*/
if (db == NULL ||
tmp->entry->last_autovac_time < db->entry->last_autovac_time)
db = tmp;
int32 oldest = 0;
whole_db = true;
foreach(cell, dblist)
{
autovac_dbase *tmp = lfirst(cell);
int32 age = (int32) (nextXid - tmp->minxid);
if (age > oldest)
{
oldest = age;
db = tmp;
}
}
Assert(db);
}
if (db)
......@@ -454,7 +486,7 @@ autovac_get_database_list(void)
FILE *db_file;
Oid db_id;
Oid db_tablespace;
TransactionId db_frozenxid;
TransactionId db_minxid;
TransactionId db_vacuumxid;
filename = database_getflatfilename();
......@@ -465,7 +497,7 @@ autovac_get_database_list(void)
errmsg("could not open file \"%s\": %m", filename)));
while (read_pg_database_line(db_file, thisname, &db_id,
&db_tablespace, &db_frozenxid,
&db_tablespace, &db_minxid,
&db_vacuumxid))
{
autovac_dbase *db;
......@@ -474,7 +506,7 @@ autovac_get_database_list(void)
db->oid = db_id;
db->name = pstrdup(thisname);
db->frozenxid = db_frozenxid;
db->minxid = db_minxid;
db->vacuumxid = db_vacuumxid;
/* these get set later: */
db->entry = NULL;
......
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.490 2006/06/29 20:00:08 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.491 2006/07/10 16:20:51 alvherre Exp $
*
* NOTES
*
......@@ -3417,6 +3417,16 @@ sigusr1_handler(SIGNAL_ARGS)
kill(SysLoggerPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC))
{
/* start one iteration of the autovacuum daemon */
if (Shutdown == NoShutdown)
{
Assert(!AutoVacuumingActive());
AutoVacPID = autovac_start();
}
}
PG_SETMASK(&UnBlockSig);
errno = save_errno;
......
......@@ -23,7 +23,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.18 2006/05/04 16:07:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.19 2006/07/10 16:20:51 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -163,7 +163,7 @@ name_okay(const char *str)
/*
* write_database_file: update the flat database file
*
* A side effect is to determine the oldest database's datfrozenxid
* A side effect is to determine the oldest database's datminxid
* so we can set or update the XID wrap limit.
*/
static void
......@@ -177,7 +177,7 @@ write_database_file(Relation drel)
HeapScanDesc scan;
HeapTuple tuple;
NameData oldest_datname;
TransactionId oldest_datfrozenxid = InvalidTransactionId;
TransactionId oldest_datminxid = InvalidTransactionId;
/*
* Create a temporary filename to be renamed later. This prevents the
......@@ -208,27 +208,27 @@ write_database_file(Relation drel)
char *datname;
Oid datoid;
Oid dattablespace;
TransactionId datfrozenxid,
TransactionId datminxid,
datvacuumxid;
datname = NameStr(dbform->datname);
datoid = HeapTupleGetOid(tuple);
dattablespace = dbform->dattablespace;
datfrozenxid = dbform->datfrozenxid;
datminxid = dbform->datminxid;
datvacuumxid = dbform->datvacuumxid;
/*
* Identify the oldest datfrozenxid, ignoring databases that are not
* Identify the oldest datminxid, ignoring databases that are not
* connectable (we assume they are safely frozen). This must match
* the logic in vac_truncate_clog() in vacuum.c.
*/
if (dbform->datallowconn &&
TransactionIdIsNormal(datfrozenxid))
TransactionIdIsNormal(datminxid))
{
if (oldest_datfrozenxid == InvalidTransactionId ||
TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
if (oldest_datminxid == InvalidTransactionId ||
TransactionIdPrecedes(datminxid, oldest_datminxid))
{
oldest_datfrozenxid = datfrozenxid;
oldest_datminxid = datminxid;
namestrcpy(&oldest_datname, datname);
}
}
......@@ -244,14 +244,14 @@ write_database_file(Relation drel)
}
/*
* The file format is: "dbname" oid tablespace frozenxid vacuumxid
* The file format is: "dbname" oid tablespace minxid vacuumxid
*
* The xids are not needed for backend startup, but are of use to
* autovacuum, and might also be helpful for forensic purposes.
*/
fputs_quote(datname, fp);
fprintf(fp, " %u %u %u %u\n",
datoid, dattablespace, datfrozenxid, datvacuumxid);
datoid, dattablespace, datminxid, datvacuumxid);
}
heap_endscan(scan);
......@@ -272,10 +272,10 @@ write_database_file(Relation drel)
tempname, filename)));
/*
* Set the transaction ID wrap limit using the oldest datfrozenxid
* Set the transaction ID wrap limit using the oldest datminxid
*/
if (oldest_datfrozenxid != InvalidTransactionId)
SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
if (oldest_datminxid != InvalidTransactionId)
SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
}
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.57 2006/03/05 15:58:53 momjian Exp $
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.58 2006/07/10 16:20:51 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -23,6 +23,7 @@
* always be considered valid.
*
* FirstNormalTransactionId is the first "normal" transaction id.
* Note: if you need to change it, you must change it in pg_class.h as well.
* ----------------
*/
#define InvalidTransactionId ((TransactionId) 0)
......@@ -123,7 +124,7 @@ extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
/* in transam/varsup.c */
extern TransactionId GetNewTransactionId(bool isSubXact);
extern TransactionId ReadNewTransactionId(void);
extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
extern void SetTransactionIdLimit(TransactionId oldest_datminxid,
Name oldest_datname);
extern Oid GetNewObjectId(void);
......
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.336 2006/07/03 22:45:40 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.337 2006/07/10 16:20:51 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200607021
#define CATALOG_VERSION_NO 200607101
#endif
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.122 2006/07/03 22:45:40 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.123 2006/07/10 16:20:51 alvherre Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -404,8 +404,10 @@ DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
{ 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1259, {"reloptions"}, 1009, -1, -1, 26, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
{ 1259, {"relminxid"}, 28, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1259, {"relvacuumxid"}, 28, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1259, {"relacl"}, 1034, -1, -1, 27, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1259, {"reloptions"}, 1009, -1, -1, 28, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
......@@ -431,8 +433,10 @@ DATA(insert ( 1259 relhasoids 16 -1 1 21 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 reloptions 1009 -1 -1 26 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 relminxid 28 -1 4 25 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 relvacuumxid 28 -1 4 26 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 relacl 1034 -1 -1 27 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 reloptions 1009 -1 -1 28 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.94 2006/07/03 22:45:40 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.95 2006/07/10 16:20:51 alvherre Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -65,6 +65,8 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP
bool relhaspkey; /* has PRIMARY KEY index */
bool relhasrules; /* has associated rules */
bool relhassubclass; /* has derived classes */
TransactionId relminxid; /* minimum Xid present in table */
TransactionId relvacuumxid; /* Xid used as last vacuum OldestXmin */
/*
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
......@@ -78,7 +80,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP
/* Size of fixed part of pg_class tuples, not counting var-length fields */
#define CLASS_TUPLE_SIZE \
(offsetof(FormData_pg_class,relhassubclass) + sizeof(bool))
(offsetof(FormData_pg_class,relvacuumxid) + sizeof(TransactionId))
/* ----------------
* Form_pg_class corresponds to a pointer to a tuple with
......@@ -92,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class;
* ----------------
*/
#define Natts_pg_class 26
#define Natts_pg_class 28
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
......@@ -117,8 +119,10 @@ typedef FormData_pg_class *Form_pg_class;
#define Anum_pg_class_relhaspkey 22
#define Anum_pg_class_relhasrules 23
#define Anum_pg_class_relhassubclass 24
#define Anum_pg_class_relacl 25
#define Anum_pg_class_reloptions 26
#define Anum_pg_class_relminxid 25
#define Anum_pg_class_relvacuumxid 26
#define Anum_pg_class_relacl 27
#define Anum_pg_class_reloptions 28
/* ----------------
* initial contents of pg_class
......@@ -128,13 +132,14 @@ typedef FormData_pg_class *Form_pg_class;
* ----------------
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ _null_ ));
/* Note: the "3" here stands for FirstNormalTransactionId */
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ _null_ ));
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 3 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f _null_ _null_ ));
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 26 0 0 0 0 0 t f f f _null_ _null_ ));
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 28 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.40 2006/03/05 15:58:54 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.41 2006/07/10 16:20:51 alvherre Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -43,7 +43,7 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION
int4 datconnlimit; /* max connections allowed (-1=no limit) */
Oid datlastsysoid; /* highest OID to consider a system OID */
TransactionId datvacuumxid; /* all XIDs before this are vacuumed */
TransactionId datfrozenxid; /* all XIDs before this are frozen */
TransactionId datminxid; /* minimum XID present anywhere in the DB */
Oid dattablespace; /* default table space for this DB */
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
......@@ -69,7 +69,7 @@ typedef FormData_pg_database *Form_pg_database;
#define Anum_pg_database_datconnlimit 6
#define Anum_pg_database_datlastsysoid 7
#define Anum_pg_database_datvacuumxid 8
#define Anum_pg_database_datfrozenxid 9
#define Anum_pg_database_datminxid 9
#define Anum_pg_database_dattablespace 10
#define Anum_pg_database_datconfig 11
#define Anum_pg_database_datacl 12
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.63 2006/03/05 15:58:55 momjian Exp $
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.64 2006/07/10 16:20:51 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -117,12 +117,15 @@ extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
extern void vac_update_relstats(Oid relid,
BlockNumber num_pages,
double num_tuples,
bool hasindex);
bool hasindex,
TransactionId minxid,
TransactionId vacuumxid);
extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
TransactionId *oldestXmin,
TransactionId *freezeLimit);
extern bool vac_is_partial_index(Relation indrel);
extern void vacuum_delay_point(void);
extern TransactionId vactuple_get_minxid(HeapTuple tuple);
/* in commands/vacuumlazy.c */
extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
......
......@@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.42 2006/03/06 17:41:44 momjian Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.43 2006/07/10 16:20:52 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -40,7 +40,7 @@ extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
Oid *dbtablespace, TransactionId *dbfrozenxid,
Oid *dbtablespace, TransactionId *dbminxid,
TransactionId *dbvacuumxid);
#endif /* HBA_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.14 2006/03/05 15:58:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.15 2006/07/10 16:20:52 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -26,6 +26,7 @@ typedef enum
PMSIGNAL_WAKEN_CHILDREN, /* send a SIGUSR1 signal to all backends */
PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */
PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */
PMSIGNAL_START_AUTOVAC, /* start an autovacuum iteration */
NUM_PMSIGNALS /* Must be last value of enum! */
} PMSignalReason;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册