mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +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/access/heap/heapam.c,v 1.237 2007/08/14 17:35:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.238 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -1632,12 +1632,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
|
||||
MarkBufferDirty(buffer);
|
||||
|
||||
/* XLOG stuff */
|
||||
if (relation->rd_istemp)
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
else if (use_wal)
|
||||
if (use_wal && !relation->rd_istemp)
|
||||
{
|
||||
xl_heap_insert xlrec;
|
||||
xl_heap_header xlhdr;
|
||||
@@ -1947,11 +1942,6 @@ l1:
|
||||
PageSetLSN(dp, recptr);
|
||||
PageSetTLI(dp, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
@@ -2403,11 +2393,6 @@ l2:
|
||||
PageSetLSN(BufferGetPage(buffer), recptr);
|
||||
PageSetTLI(BufferGetPage(buffer), ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
@@ -2924,11 +2909,6 @@ l3:
|
||||
PageSetLSN(dp, recptr);
|
||||
PageSetTLI(dp, ThisTimeLineID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No XLOG record, but still need to flag that XID exists on disk */
|
||||
MyXactMadeTempRelUpdate = true;
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.6 2007/08/01 22:45:07 tgl Exp $
|
||||
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.7 2007/09/05 18:10:47 tgl Exp $
|
||||
|
||||
The Transaction System
|
||||
----------------------
|
||||
@@ -187,16 +187,29 @@ Another difference is that BeginInternalSubtransaction is allowed when no
|
||||
explicit transaction block has been established, while DefineSavepoint is not.
|
||||
|
||||
|
||||
Subtransaction numbering
|
||||
------------------------
|
||||
Transaction and subtransaction numbering
|
||||
----------------------------------------
|
||||
|
||||
A top-level transaction is always given a TransactionId (XID) as soon as it is
|
||||
created. This is necessary for a number of reasons, notably XMIN bookkeeping
|
||||
for VACUUM. However, a subtransaction doesn't need its own XID unless it
|
||||
(or one of its child subxacts) writes tuples into the database. Therefore,
|
||||
we postpone assigning XIDs to subxacts until and unless they call
|
||||
GetCurrentTransactionId. The subsidiary actions of obtaining a lock on the
|
||||
XID and and entering it into pg_subtrans and PG_PROC are done at the same time.
|
||||
Transactions and subtransactions are assigned permanent XIDs only when/if
|
||||
they first do something that requires one --- typically, insert/update/delete
|
||||
a tuple, though there are a few other places that need an XID assigned.
|
||||
If a subtransaction requires an XID, we always first assign one to its
|
||||
parent. This maintains the invariant that child transactions have XIDs later
|
||||
than their parents, which is assumed in a number of places.
|
||||
|
||||
The subsidiary actions of obtaining a lock on the XID and and entering it into
|
||||
pg_subtrans and PG_PROC are done at the time it is assigned.
|
||||
|
||||
A transaction that has no XID still needs to be identified for various
|
||||
purposes, notably holding locks. For this purpose we assign a "virtual
|
||||
transaction ID" or VXID to each top-level transaction. VXIDs are formed from
|
||||
two fields, the backendID and a backend-local counter; this arrangement allows
|
||||
assignment of a new VXID at transaction start without any contention for
|
||||
shared memory. To ensure that a VXID isn't re-used too soon after backend
|
||||
exit, we store the last local counter value into shared memory at backend
|
||||
exit, and initialize it from the previous value for the same backendID slot
|
||||
at backend start. All these counters go back to zero at shared memory
|
||||
re-initialization, but that's OK because VXIDs never appear anywhere on-disk.
|
||||
|
||||
Internally, a backend needs a way to identify subtransactions whether or not
|
||||
they have XIDs; but this need only lasts as long as the parent top transaction
|
||||
@@ -204,7 +217,8 @@ endures. Therefore, we have SubTransactionId, which is somewhat like
|
||||
CommandId in that it's generated from a counter that we reset at the start of
|
||||
each top transaction. The top-level transaction itself has SubTransactionId 1,
|
||||
and subtransactions have IDs 2 and up. (Zero is reserved for
|
||||
InvalidSubTransactionId.)
|
||||
InvalidSubTransactionId.) Note that subtransactions do not have their
|
||||
own VXIDs; they use the parent top transaction's VXID.
|
||||
|
||||
|
||||
pg_clog and pg_subtrans
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.43 2007/08/01 22:45:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.44 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -423,10 +423,6 @@ CLOGPagePrecedes(int page1, int page2)
|
||||
|
||||
/*
|
||||
* Write a ZEROPAGE xlog record
|
||||
*
|
||||
* Note: xlog record is marked as outside transaction control, since we
|
||||
* want it to be redone whether the invoking transaction commits or not.
|
||||
* (Besides which, this is normally done just before entering a transaction.)
|
||||
*/
|
||||
static void
|
||||
WriteZeroPageXlogRec(int pageno)
|
||||
@@ -437,7 +433,7 @@ WriteZeroPageXlogRec(int pageno)
|
||||
rdata.len = sizeof(int);
|
||||
rdata.buffer = InvalidBuffer;
|
||||
rdata.next = NULL;
|
||||
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
|
||||
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE, &rdata);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -445,9 +441,6 @@ WriteZeroPageXlogRec(int pageno)
|
||||
*
|
||||
* We must flush the xlog record to disk before returning --- see notes
|
||||
* in TruncateCLOG().
|
||||
*
|
||||
* Note: xlog record is marked as outside transaction control, since we
|
||||
* want it to be redone whether the invoking transaction commits or not.
|
||||
*/
|
||||
static void
|
||||
WriteTruncateXlogRec(int pageno)
|
||||
@@ -459,7 +452,7 @@ WriteTruncateXlogRec(int pageno)
|
||||
rdata.len = sizeof(int);
|
||||
rdata.buffer = InvalidBuffer;
|
||||
rdata.next = NULL;
|
||||
recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE | XLOG_NO_TRAN, &rdata);
|
||||
recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE, &rdata);
|
||||
XLogFlush(recptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.24 2007/08/01 22:45:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.25 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1842,9 +1842,6 @@ MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
|
||||
/*
|
||||
* Write an xlog record reflecting the zeroing of either a MEMBERs or
|
||||
* OFFSETs page (info shows which)
|
||||
*
|
||||
* Note: xlog record is marked as outside transaction control, since we
|
||||
* want it to be redone whether the invoking transaction commits or not.
|
||||
*/
|
||||
static void
|
||||
WriteMZeroPageXlogRec(int pageno, uint8 info)
|
||||
@@ -1855,7 +1852,7 @@ WriteMZeroPageXlogRec(int pageno, uint8 info)
|
||||
rdata.len = sizeof(int);
|
||||
rdata.buffer = InvalidBuffer;
|
||||
rdata.next = NULL;
|
||||
(void) XLogInsert(RM_MULTIXACT_ID, info | XLOG_NO_TRAN, &rdata);
|
||||
(void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.32 2007/08/01 22:45:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.33 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Each global transaction is associated with a global transaction
|
||||
@@ -274,9 +274,11 @@ MarkAsPreparing(TransactionId xid, const char *gid,
|
||||
MemSet(&gxact->proc, 0, sizeof(PGPROC));
|
||||
SHMQueueElemInit(&(gxact->proc.links));
|
||||
gxact->proc.waitStatus = STATUS_OK;
|
||||
gxact->proc.lxid = InvalidLocalTransactionId;
|
||||
gxact->proc.xid = xid;
|
||||
gxact->proc.xmin = InvalidTransactionId;
|
||||
gxact->proc.pid = 0;
|
||||
gxact->proc.backendId = InvalidBackendId;
|
||||
gxact->proc.databaseId = databaseid;
|
||||
gxact->proc.roleId = owner;
|
||||
gxact->proc.inCommit = false;
|
||||
@@ -813,8 +815,8 @@ StartPrepare(GlobalTransaction gxact)
|
||||
hdr.prepared_at = gxact->prepared_at;
|
||||
hdr.owner = gxact->owner;
|
||||
hdr.nsubxacts = xactGetCommittedChildren(&children);
|
||||
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
|
||||
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
|
||||
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels, NULL);
|
||||
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels, NULL);
|
||||
StrNCpy(hdr.gid, gxact->gid, GIDSIZE);
|
||||
|
||||
save_state_data(&hdr, sizeof(TwoPhaseFileHeader));
|
||||
@@ -1702,9 +1704,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
|
||||
}
|
||||
rdata[lastrdata].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_XACT_ID,
|
||||
XLOG_XACT_COMMIT_PREPARED | XLOG_NO_TRAN,
|
||||
rdata);
|
||||
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT_PREPARED, rdata);
|
||||
|
||||
/*
|
||||
* We don't currently try to sleep before flush here ... nor is there
|
||||
@@ -1784,9 +1784,7 @@ RecordTransactionAbortPrepared(TransactionId xid,
|
||||
}
|
||||
rdata[lastrdata].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_XACT_ID,
|
||||
XLOG_XACT_ABORT_PREPARED | XLOG_NO_TRAN,
|
||||
rdata);
|
||||
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT_PREPARED, rdata);
|
||||
|
||||
/* Always flush, since we're about to remove the 2PC state file */
|
||||
XLogFlush(recptr);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.279 2007/08/28 23:17:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.280 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -154,38 +154,16 @@ static TimeLineID recoveryTargetTLI;
|
||||
static List *expectedTLIs;
|
||||
static TimeLineID curFileTLI;
|
||||
|
||||
/*
|
||||
* MyLastRecPtr points to the start of the last XLOG record inserted by the
|
||||
* current transaction. If MyLastRecPtr.xrecoff == 0, then the current
|
||||
* xact hasn't yet inserted any transaction-controlled XLOG records.
|
||||
*
|
||||
* Note that XLOG records inserted outside transaction control are not
|
||||
* reflected into MyLastRecPtr. They do, however, cause MyXactMadeXLogEntry
|
||||
* to be set true. The latter can be used to test whether the current xact
|
||||
* made any loggable changes (including out-of-xact changes, such as
|
||||
* sequence updates).
|
||||
*
|
||||
* When we insert/update/delete a tuple in a temporary relation, we do not
|
||||
* make any XLOG record, since we don't care about recovering the state of
|
||||
* the temp rel after a crash. However, we will still need to remember
|
||||
* whether our transaction committed or aborted in that case. So, we must
|
||||
* set MyXactMadeTempRelUpdate true to indicate that the XID will be of
|
||||
* interest later.
|
||||
*/
|
||||
XLogRecPtr MyLastRecPtr = {0, 0};
|
||||
|
||||
bool MyXactMadeXLogEntry = false;
|
||||
|
||||
bool MyXactMadeTempRelUpdate = false;
|
||||
|
||||
/*
|
||||
* ProcLastRecPtr points to the start of the last XLOG record inserted by the
|
||||
* current backend. It is updated for all inserts, transaction-controlled
|
||||
* or not. ProcLastRecEnd is similar but points to end+1 of last record.
|
||||
* current backend. It is updated for all inserts. XactLastRecEnd points to
|
||||
* end+1 of the last record, and is reset when we end a top-level transaction,
|
||||
* or start a new one; so it can be used to tell if the current transaction has
|
||||
* created any XLOG records.
|
||||
*/
|
||||
static XLogRecPtr ProcLastRecPtr = {0, 0};
|
||||
|
||||
XLogRecPtr ProcLastRecEnd = {0, 0};
|
||||
XLogRecPtr XactLastRecEnd = {0, 0};
|
||||
|
||||
/*
|
||||
* RedoRecPtr is this backend's local copy of the REDO record pointer
|
||||
@@ -488,15 +466,10 @@ XLogInsert(RmgrId rmid, uint8 info, XLogRecData *rdata)
|
||||
bool updrqst;
|
||||
bool doPageWrites;
|
||||
bool isLogSwitch = (rmid == RM_XLOG_ID && info == XLOG_SWITCH);
|
||||
bool no_tran = (rmid == RM_XLOG_ID);
|
||||
|
||||
/* info's high bits are reserved for use by me */
|
||||
if (info & XLR_INFO_MASK)
|
||||
{
|
||||
if ((info & XLR_INFO_MASK) != XLOG_NO_TRAN)
|
||||
elog(PANIC, "invalid xlog info mask %02X", (info & XLR_INFO_MASK));
|
||||
no_tran = true;
|
||||
info &= ~XLR_INFO_MASK;
|
||||
}
|
||||
elog(PANIC, "invalid xlog info mask %02X", info);
|
||||
|
||||
/*
|
||||
* In bootstrap mode, we don't actually log anything but XLOG resources;
|
||||
@@ -856,11 +829,8 @@ begin:;
|
||||
#endif
|
||||
|
||||
/* Record begin of record in appropriate places */
|
||||
if (!no_tran)
|
||||
MyLastRecPtr = RecPtr;
|
||||
ProcLastRecPtr = RecPtr;
|
||||
Insert->PrevRecord = RecPtr;
|
||||
MyXactMadeXLogEntry = true;
|
||||
|
||||
Insert->currpos += SizeOfXLogRecord;
|
||||
freespace -= SizeOfXLogRecord;
|
||||
@@ -1018,7 +988,7 @@ begin:;
|
||||
SpinLockRelease(&xlogctl->info_lck);
|
||||
}
|
||||
|
||||
ProcLastRecEnd = RecPtr;
|
||||
XactLastRecEnd = RecPtr;
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user