mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Implement lazy XID allocation: transactions that do not modify any database
rows will normally never obtain an XID at all. We already did things this way for subtransactions, but this patch extends the concept to top-level transactions. In applications where there are lots of short read-only transactions, this should improve performance noticeably; not so much from removal of the actual XID-assignments, as from reduction of overhead that's driven by the rate of XID consumption. We add a concept of a "virtual transaction ID" so that active transactions can be uniquely identified even if they don't have a regular XID. This is a much lighter-weight concept: uniqueness of VXIDs is only guaranteed over the short term, and no on-disk record is made about them. Florian Pflug, with some editorialization by Tom.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.162 2007/08/25 19:08:19 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.163 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -38,6 +38,7 @@
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "storage/procarray.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
@ -126,9 +127,8 @@ DefineIndex(RangeVar *heapRelation,
|
||||
int16 *coloptions;
|
||||
IndexInfo *indexInfo;
|
||||
int numberOfAttributes;
|
||||
List *old_xact_list;
|
||||
ListCell *lc;
|
||||
uint32 ixcnt;
|
||||
VirtualTransactionId *old_lockholders;
|
||||
VirtualTransactionId *old_snapshots;
|
||||
LockRelId heaprelid;
|
||||
LOCKTAG heaplocktag;
|
||||
Snapshot snapshot;
|
||||
@ -484,24 +484,36 @@ DefineIndex(RangeVar *heapRelation,
|
||||
* xacts that open the table for writing after this point; they will see
|
||||
* the new index when they open it.
|
||||
*
|
||||
* Note: the reason we use actual lock acquisition here, rather than
|
||||
* just checking the ProcArray and sleeping, is that deadlock is possible
|
||||
* if one of the transactions in question is blocked trying to acquire
|
||||
* an exclusive lock on our table. The lock code will detect deadlock
|
||||
* and error out properly.
|
||||
*
|
||||
* Note: GetLockConflicts() never reports our own xid, hence we need not
|
||||
* check for that.
|
||||
* check for that. Also, prepared xacts are not reported, which is
|
||||
* fine since they certainly aren't going to do anything more.
|
||||
*/
|
||||
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
|
||||
old_xact_list = GetLockConflicts(&heaplocktag, ShareLock);
|
||||
old_lockholders = GetLockConflicts(&heaplocktag, ShareLock);
|
||||
|
||||
foreach(lc, old_xact_list)
|
||||
while (VirtualTransactionIdIsValid(*old_lockholders))
|
||||
{
|
||||
TransactionId xid = lfirst_xid(lc);
|
||||
|
||||
XactLockTableWait(xid);
|
||||
VirtualXactLockTableWait(*old_lockholders);
|
||||
old_lockholders++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now take the "reference snapshot" that will be used by validate_index()
|
||||
* to filter candidate tuples. All other transactions running at this
|
||||
* time will have to be out-waited before we can commit, because we can't
|
||||
* guarantee that tuples deleted just before this will be in the index.
|
||||
* to filter candidate tuples. Beware! There might be still snapshots
|
||||
* in use that treat some transaction as in-progress that our reference
|
||||
* snapshot treats as committed. If such a recently-committed transaction
|
||||
* deleted tuples in the table, we will not include them in the index; yet
|
||||
* those transactions which see the deleting one as still-in-progress will
|
||||
* expect them to be there once we mark the index as valid.
|
||||
*
|
||||
* We solve this by waiting for all endangered transactions to exit before
|
||||
* we mark the index as valid.
|
||||
*
|
||||
* We also set ActiveSnapshot to this snap, since functions in indexes may
|
||||
* need a snapshot.
|
||||
@ -518,14 +530,21 @@ DefineIndex(RangeVar *heapRelation,
|
||||
* The index is now valid in the sense that it contains all currently
|
||||
* interesting tuples. But since it might not contain tuples deleted just
|
||||
* before the reference snap was taken, we have to wait out any
|
||||
* transactions older than the reference snap. We can do this by waiting
|
||||
* for each xact explicitly listed in the snap.
|
||||
* transactions that might have older snapshots. Obtain a list of
|
||||
* VXIDs of such transactions, and wait for them individually.
|
||||
*
|
||||
* Note: GetSnapshotData() never stores our own xid into a snap, hence we
|
||||
* need not check for that.
|
||||
* We can exclude any running transactions that have xmin >= the xmax of
|
||||
* our reference snapshot, since they are clearly not interested in any
|
||||
* missing older tuples. Also, GetCurrentVirtualXIDs never reports our
|
||||
* own vxid, so we need not check for that.
|
||||
*/
|
||||
for (ixcnt = 0; ixcnt < snapshot->xcnt; ixcnt++)
|
||||
XactLockTableWait(snapshot->xip[ixcnt]);
|
||||
old_snapshots = GetCurrentVirtualXIDs(ActiveSnapshot->xmax);
|
||||
|
||||
while (VirtualTransactionIdIsValid(*old_snapshots))
|
||||
{
|
||||
VirtualXactLockTableWait(*old_snapshots);
|
||||
old_snapshots++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Index can now be marked valid -- update its pg_index entry
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.143 2007/02/01 19:10:26 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.144 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,6 +25,7 @@
|
||||
#include "commands/tablecmds.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@ -63,7 +64,7 @@ typedef struct SeqTableData
|
||||
{
|
||||
struct SeqTableData *next; /* link to next SeqTable object */
|
||||
Oid relid; /* pg_class OID of this sequence */
|
||||
TransactionId xid; /* xact in which we last did a seq op */
|
||||
LocalTransactionId lxid; /* xact in which we last did a seq op */
|
||||
int64 last; /* value last returned by nextval */
|
||||
int64 cached; /* last value already cached for nextval */
|
||||
/* if last != cached, we have not used up all the cached values */
|
||||
@ -282,7 +283,7 @@ DefineSequence(CreateSeqStmt *seq)
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
|
||||
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
@ -366,7 +367,7 @@ AlterSequence(AlterSeqStmt *stmt)
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
|
||||
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
@ -594,7 +595,7 @@ nextval_internal(Oid relid)
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
|
||||
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
@ -764,7 +765,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
|
||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
|
||||
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
@ -825,10 +826,10 @@ setval3_oid(PG_FUNCTION_ARGS)
|
||||
static Relation
|
||||
open_share_lock(SeqTable seq)
|
||||
{
|
||||
TransactionId thisxid = GetTopTransactionId();
|
||||
LocalTransactionId thislxid = MyProc->lxid;
|
||||
|
||||
/* Get the lock if not already held in this xact */
|
||||
if (seq->xid != thisxid)
|
||||
if (seq->lxid != thislxid)
|
||||
{
|
||||
ResourceOwner currentOwner;
|
||||
|
||||
@ -848,7 +849,7 @@ open_share_lock(SeqTable seq)
|
||||
CurrentResourceOwner = currentOwner;
|
||||
|
||||
/* Flag that we have a lock in the current xact */
|
||||
seq->xid = thisxid;
|
||||
seq->lxid = thislxid;
|
||||
}
|
||||
|
||||
/* We now know we have AccessShareLock, and can safely open the rel */
|
||||
@ -891,7 +892,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
|
||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||
errmsg("out of memory")));
|
||||
elm->relid = relid;
|
||||
elm->xid = InvalidTransactionId;
|
||||
elm->lxid = InvalidLocalTransactionId;
|
||||
/* increment is set to 0 until we do read_info (see currval) */
|
||||
elm->last = elm->cached = elm->increment = 0;
|
||||
elm->next = seqtab;
|
||||
|
@ -13,7 +13,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.355 2007/08/13 19:08:26 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.356 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -2601,14 +2601,6 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* No XLOG record, but still need to flag that XID exists on
|
||||
* disk
|
||||
*/
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
@ -2761,13 +2753,6 @@ move_chain_tuple(Relation rel,
|
||||
PageSetLSN(dst_page, recptr);
|
||||
PageSetTLI(dst_page, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* No XLOG record, but still need to flag that XID exists on disk
|
||||
*/
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
@ -2868,13 +2853,6 @@ move_plain_tuple(Relation rel,
|
||||
PageSetLSN(dst_page, recptr);
|
||||
PageSetTLI(dst_page, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* No XLOG record, but still need to flag that XID exists on disk
|
||||
*/
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
@ -3070,11 +3048,6 @@ vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage)
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.90 2007/05/30 20:11:57 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.91 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -658,11 +658,6 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
|
Reference in New Issue
Block a user