mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-28 01:21:49 +02:00
For multi-table ANALYZE, use per-table transactions when possible
(ie, when not inside a transaction block), so that we can avoid holding locks longer than necessary. Per trouble report from Philip Warner.
This commit is contained in:
parent
e26c403fd0
commit
4d86ae4260
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.166 2004/05/21 05:07:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.167 2004/05/22 23:14:37 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Transaction aborts can now occur two ways:
|
* Transaction aborts can now occur two ways:
|
||||||
@ -1462,6 +1462,36 @@ RequireTransactionChain(void *stmtNode, const char *stmtType)
|
|||||||
stmtType)));
|
stmtType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsInTransactionChain
|
||||||
|
*
|
||||||
|
* This routine is for statements that need to behave differently inside
|
||||||
|
* a transaction block than when running as single commands. ANALYZE is
|
||||||
|
* currently the only example.
|
||||||
|
*
|
||||||
|
* stmtNode: pointer to parameter block for statement; this is used in
|
||||||
|
* a very klugy way to determine whether we are inside a function.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsInTransactionChain(void *stmtNode)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Return true on same conditions that would make PreventTransactionChain
|
||||||
|
* error out
|
||||||
|
*/
|
||||||
|
if (IsTransactionBlock())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!MemoryContextContains(QueryContext, stmtNode))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
|
||||||
|
CurrentTransactionState->blockState != TBLOCK_STARTED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register or deregister callback functions for end-of-xact cleanup
|
* Register or deregister callback functions for end-of-xact cleanup
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.276 2004/05/21 16:08:46 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.277 2004/05/22 23:14:38 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -161,7 +161,9 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
MemoryContext anl_context = NULL;
|
MemoryContext anl_context = NULL;
|
||||||
TransactionId initialOldestXmin = InvalidTransactionId;
|
TransactionId initialOldestXmin = InvalidTransactionId;
|
||||||
TransactionId initialFreezeLimit = InvalidTransactionId;
|
TransactionId initialFreezeLimit = InvalidTransactionId;
|
||||||
bool all_rels;
|
bool all_rels,
|
||||||
|
in_outer_xact,
|
||||||
|
use_own_xacts;
|
||||||
List *relations,
|
List *relations,
|
||||||
*cur;
|
*cur;
|
||||||
|
|
||||||
@ -177,10 +179,23 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
* Furthermore, the forced commit that occurs before truncating the
|
* Furthermore, the forced commit that occurs before truncating the
|
||||||
* relation's file would have the effect of committing the rest of 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
|
* user's transaction too, which would certainly not be the desired
|
||||||
* behavior.
|
* behavior. (This only applies to VACUUM FULL, though. We could
|
||||||
|
* in theory run lazy VACUUM inside a transaction block, but we choose
|
||||||
|
* to disallow that case because we'd rather commit as soon as possible
|
||||||
|
* after finishing the vacuum. This is mainly so that we can let go the
|
||||||
|
* AccessExclusiveLock that we may be holding.)
|
||||||
|
*
|
||||||
|
* ANALYZE (without VACUUM) can run either way.
|
||||||
*/
|
*/
|
||||||
if (vacstmt->vacuum)
|
if (vacstmt->vacuum)
|
||||||
|
{
|
||||||
PreventTransactionChain((void *) vacstmt, stmttype);
|
PreventTransactionChain((void *) vacstmt, stmttype);
|
||||||
|
in_outer_xact = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
in_outer_xact = IsInTransactionChain((void *) vacstmt);
|
||||||
|
}
|
||||||
|
|
||||||
/* Turn vacuum cost accounting on or off */
|
/* Turn vacuum cost accounting on or off */
|
||||||
VacuumCostActive = (VacuumCostNaptime > 0);
|
VacuumCostActive = (VacuumCostNaptime > 0);
|
||||||
@ -205,55 +220,13 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
ALLOCSET_DEFAULT_INITSIZE,
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
ALLOCSET_DEFAULT_MAXSIZE);
|
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 */
|
/* Assume we are processing everything unless one table is mentioned */
|
||||||
all_rels = (vacstmt->relation == NULL);
|
all_rels = (vacstmt->relation == NULL);
|
||||||
|
|
||||||
/* Build list of relations to process (note this lives in vac_context) */
|
/* Build list of relations to process (note this lives in vac_context) */
|
||||||
relations = get_rel_oids(vacstmt->relation, stmttype);
|
relations = get_rel_oids(vacstmt->relation, stmttype);
|
||||||
|
|
||||||
/*
|
if (vacstmt->vacuum && all_rels)
|
||||||
* 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.
|
* It's a database-wide VACUUM.
|
||||||
@ -280,6 +253,56 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
&initialFreezeLimit);
|
&initialFreezeLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decide whether we need to start/commit our own transactions.
|
||||||
|
*
|
||||||
|
* For VACUUM (with or without ANALYZE): always do so, so that we
|
||||||
|
* can release locks as soon as possible. (We could possibly use the
|
||||||
|
* outer transaction for a one-table VACUUM, but handling TOAST tables
|
||||||
|
* would be problematic.)
|
||||||
|
*
|
||||||
|
* For ANALYZE (no VACUUM): if inside a transaction block, we cannot
|
||||||
|
* start/commit our own transactions. Also, there's no need to do so
|
||||||
|
* if only processing one relation. For multiple relations when not
|
||||||
|
* within a transaction block, use own transactions so we can release
|
||||||
|
* locks sooner.
|
||||||
|
*/
|
||||||
|
if (vacstmt->vacuum)
|
||||||
|
{
|
||||||
|
use_own_xacts = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(vacstmt->analyze);
|
||||||
|
if (in_outer_xact)
|
||||||
|
use_own_xacts = false;
|
||||||
|
else if (length(relations) > 1)
|
||||||
|
use_own_xacts = true;
|
||||||
|
else
|
||||||
|
use_own_xacts = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are running ANALYZE without per-table transactions, we'll
|
||||||
|
* need a memory context with table lifetime.
|
||||||
|
*/
|
||||||
|
if (!use_own_xacts)
|
||||||
|
anl_context = AllocSetContextCreate(PortalContext,
|
||||||
|
"Analyze",
|
||||||
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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().
|
||||||
|
*/
|
||||||
|
if (use_own_xacts)
|
||||||
|
{
|
||||||
/* matches the StartTransaction in PostgresMain() */
|
/* matches the StartTransaction in PostgresMain() */
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
}
|
}
|
||||||
@ -301,13 +324,13 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
MemoryContext old_context = NULL;
|
MemoryContext old_context = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we vacuumed, use new transaction for analyze. Otherwise,
|
* If using separate xacts, start one for analyze. Otherwise,
|
||||||
* we can use the outer transaction, but we still need to call
|
* we can use the outer transaction, but we still need to call
|
||||||
* analyze_rel in a memory context that will be cleaned up on
|
* analyze_rel in a memory context that will be cleaned up on
|
||||||
* return (else we leak memory while processing multiple
|
* return (else we leak memory while processing multiple
|
||||||
* tables).
|
* tables).
|
||||||
*/
|
*/
|
||||||
if (vacstmt->vacuum)
|
if (use_own_xacts)
|
||||||
{
|
{
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
SetQuerySnapshot(); /* might be needed for functions
|
SetQuerySnapshot(); /* might be needed for functions
|
||||||
@ -326,7 +349,7 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
|
|
||||||
StrategyHintVacuum(false);
|
StrategyHintVacuum(false);
|
||||||
|
|
||||||
if (vacstmt->vacuum)
|
if (use_own_xacts)
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -339,7 +362,7 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
/*
|
/*
|
||||||
* Finish up processing.
|
* Finish up processing.
|
||||||
*/
|
*/
|
||||||
if (vacstmt->vacuum)
|
if (use_own_xacts)
|
||||||
{
|
{
|
||||||
/* here, we are not in a transaction */
|
/* here, we are not in a transaction */
|
||||||
|
|
||||||
@ -348,7 +371,10 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
* PostgresMain().
|
* PostgresMain().
|
||||||
*/
|
*/
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vacstmt->vacuum)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* If it was a database-wide VACUUM, print FSM usage statistics
|
* If it was a database-wide VACUUM, print FSM usage statistics
|
||||||
* (we don't make you be superuser to see these).
|
* (we don't make you be superuser to see these).
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.62 2004/04/05 03:11:39 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.63 2004/05/22 23:14:38 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -145,6 +145,7 @@ extern void UserAbortTransactionBlock(void);
|
|||||||
extern void AbortOutOfAnyTransaction(void);
|
extern void AbortOutOfAnyTransaction(void);
|
||||||
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
|
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
|
||||||
extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
|
extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
|
||||||
|
extern bool IsInTransactionChain(void *stmtNode);
|
||||||
extern void RegisterEOXactCallback(EOXactCallback callback, void *arg);
|
extern void RegisterEOXactCallback(EOXactCallback callback, void *arg);
|
||||||
extern void UnregisterEOXactCallback(EOXactCallback callback, void *arg);
|
extern void UnregisterEOXactCallback(EOXactCallback callback, void *arg);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user