Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 295e639

Browse files
committed
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.
1 parent 2e74c53 commit 295e639

34 files changed

+987
-739
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.156 2007/09/03 00:39:11 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.157 2007/09/05 18:10:47 tgl Exp $ -->
22
<!--
33
Documentation of the system catalogs, directed toward PostgreSQL developers
44
-->
@@ -5147,7 +5147,7 @@
51475147
There are several distinct types of lockable objects:
51485148
whole relations (e.g., tables), individual pages of relations,
51495149
individual tuples of relations,
5150-
transaction IDs,
5150+
transaction IDs (both virtual and permanent IDs),
51515151
and general database objects (identified by class OID and object OID,
51525152
in the same way as in <structname>pg_description</structname> or
51535153
<structname>pg_depend</structname>). Also, the right to extend a
@@ -5178,6 +5178,7 @@
51785178
<literal>page</>,
51795179
<literal>tuple</>,
51805180
<literal>transactionid</>,
5181+
<literal>virtualxid</>,
51815182
<literal>object</>,
51825183
<literal>userlock</>, or
51835184
<literal>advisory</>
@@ -5219,6 +5220,15 @@
52195220
Tuple number within the page, or NULL if the object is not a tuple
52205221
</entry>
52215222
</row>
5223+
<row>
5224+
<entry><structfield>virtualxid</structfield></entry>
5225+
<entry><type>text</type></entry>
5226+
<entry></entry>
5227+
<entry>
5228+
Virtual ID of a transaction, or NULL if the object is not a
5229+
virtual transaction ID
5230+
</entry>
5231+
</row>
52225232
<row>
52235233
<entry><structfield>transactionid</structfield></entry>
52245234
<entry><type>xid</type></entry>
@@ -5257,11 +5267,11 @@
52575267
</entry>
52585268
</row>
52595269
<row>
5260-
<entry><structfield>transaction</structfield></entry>
5261-
<entry><type>xid</type></entry>
5270+
<entry><structfield>virtualtransaction</structfield></entry>
5271+
<entry><type>text</type></entry>
52625272
<entry></entry>
52635273
<entry>
5264-
ID of the transaction that is holding or awaiting this lock
5274+
Virtual ID of the transaction that is holding or awaiting this lock
52655275
</entry>
52665276
</row>
52675277
<row>
@@ -5301,10 +5311,14 @@
53015311
</para>
53025312

53035313
<para>
5304-
Every transaction holds an exclusive lock on its transaction ID for its
5305-
entire duration. If one transaction finds it necessary to wait specifically
5314+
Every transaction holds an exclusive lock on its virtual transaction ID for
5315+
its entire duration. If a permanent ID is assigned to the transaction
5316+
(which normally happens only if the transaction changes the state of the
5317+
database), it also holds an exclusive lock on its permanent transaction ID
5318+
until it ends. When one transaction finds it necessary to wait specifically
53065319
for another transaction, it does so by attempting to acquire share lock on
5307-
the other transaction ID. That will succeed only when the other transaction
5320+
the other transaction ID (either virtual or permanent ID depending on the
5321+
situation). That will succeed only when the other transaction
53085322
terminates and releases its locks.
53095323
</para>
53105324

@@ -5314,7 +5328,7 @@
53145328
and therefore row-level locks normally do not appear in this view.
53155329
If a transaction is waiting for a
53165330
row-level lock, it will usually appear in the view as waiting for the
5317-
transaction ID of the current holder of that row lock.
5331+
permanent transaction ID of the current holder of that row lock.
53185332
</para>
53195333

53205334
<para>
@@ -5350,11 +5364,10 @@
53505364
</para>
53515365

53525366
<para>
5353-
If you have enabled the statistics collector, the
5354-
<structfield>pid</structfield> column can be joined to the
5367+
The <structfield>pid</structfield> column can be joined to the
53555368
<structfield>procpid</structfield> column of the
53565369
<structname>pg_stat_activity</structname> view to get more
5357-
information on the session holding or waiting to hold the lock.
5370+
information on the session holding or waiting to hold each lock.
53585371
Also, if you are using prepared transactions, the
53595372
<structfield>transaction</> column can be joined to the
53605373
<structfield>transaction</structfield> column of the

doc/src/sgml/config.sgml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.141 2007/08/22 04:45:20 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.142 2007/09/05 18:10:47 tgl Exp $ -->
22

33
<chapter Id="runtime-config">
44
<title>Server Configuration</title>
@@ -2939,10 +2939,15 @@ SELECT * FROM parent WHERE key = 2400;
29392939
<entry>Process start time stamp</entry>
29402940
<entry>no</entry>
29412941
</row>
2942+
<row>
2943+
<entry><literal>%v</literal></entry>
2944+
<entry>Virtual transaction ID (backendID/localXID)</entry>
2945+
<entry>no</entry>
2946+
</row>
29422947
<row>
29432948
<entry><literal>%x</literal></entry>
2944-
<entry>Transaction ID</entry>
2945-
<entry>yes</entry>
2949+
<entry>Transaction ID (0 if none is assigned)</entry>
2950+
<entry>no</entry>
29462951
</row>
29472952
<row>
29482953
<entry><literal>%q</literal></entry>

src/backend/access/heap/heapam.c

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.237 2007/08/14 17:35:18 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.238 2007/09/05 18:10:47 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -1632,12 +1632,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
16321632
MarkBufferDirty(buffer);
16331633

16341634
/* XLOG stuff */
1635-
if (relation->rd_istemp)
1636-
{
1637-
/* No XLOG record, but still need to flag that XID exists on disk */
1638-
MyXactMadeTempRelUpdate = true;
1639-
}
1640-
else if (use_wal)
1635+
if (use_wal && !relation->rd_istemp)
16411636
{
16421637
xl_heap_insert xlrec;
16431638
xl_heap_header xlhdr;
@@ -1947,11 +1942,6 @@ heap_delete(Relation relation, ItemPointer tid,
19471942
PageSetLSN(dp, recptr);
19481943
PageSetTLI(dp, ThisTimeLineID);
19491944
}
1950-
else
1951-
{
1952-
/* No XLOG record, but still need to flag that XID exists on disk */
1953-
MyXactMadeTempRelUpdate = true;
1954-
}
19551945

19561946
END_CRIT_SECTION();
19571947

@@ -2403,11 +2393,6 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
24032393
PageSetLSN(BufferGetPage(buffer), recptr);
24042394
PageSetTLI(BufferGetPage(buffer), ThisTimeLineID);
24052395
}
2406-
else
2407-
{
2408-
/* No XLOG record, but still need to flag that XID exists on disk */
2409-
MyXactMadeTempRelUpdate = true;
2410-
}
24112396

24122397
END_CRIT_SECTION();
24132398

@@ -2924,11 +2909,6 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
29242909
PageSetLSN(dp, recptr);
29252910
PageSetTLI(dp, ThisTimeLineID);
29262911
}
2927-
else
2928-
{
2929-
/* No XLOG record, but still need to flag that XID exists on disk */
2930-
MyXactMadeTempRelUpdate = true;
2931-
}
29322912

29332913
END_CRIT_SECTION();
29342914

src/backend/access/transam/README

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.6 2007/08/01 22:45:07 tgl Exp $
1+
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.7 2007/09/05 18:10:47 tgl Exp $
22

33
The Transaction System
44
----------------------
@@ -187,24 +187,38 @@ Another difference is that BeginInternalSubtransaction is allowed when no
187187
explicit transaction block has been established, while DefineSavepoint is not.
188188

189189

190-
Subtransaction numbering
191-
------------------------
190+
Transaction and subtransaction numbering
191+
----------------------------------------
192192

193-
A top-level transaction is always given a TransactionId (XID) as soon as it is
194-
created. This is necessary for a number of reasons, notably XMIN bookkeeping
195-
for VACUUM. However, a subtransaction doesn't need its own XID unless it
196-
(or one of its child subxacts) writes tuples into the database. Therefore,
197-
we postpone assigning XIDs to subxacts until and unless they call
198-
GetCurrentTransactionId. The subsidiary actions of obtaining a lock on the
199-
XID and and entering it into pg_subtrans and PG_PROC are done at the same time.
193+
Transactions and subtransactions are assigned permanent XIDs only when/if
194+
they first do something that requires one --- typically, insert/update/delete
195+
a tuple, though there are a few other places that need an XID assigned.
196+
If a subtransaction requires an XID, we always first assign one to its
197+
parent. This maintains the invariant that child transactions have XIDs later
198+
than their parents, which is assumed in a number of places.
199+
200+
The subsidiary actions of obtaining a lock on the XID and and entering it into
201+
pg_subtrans and PG_PROC are done at the time it is assigned.
202+
203+
A transaction that has no XID still needs to be identified for various
204+
purposes, notably holding locks. For this purpose we assign a "virtual
205+
transaction ID" or VXID to each top-level transaction. VXIDs are formed from
206+
two fields, the backendID and a backend-local counter; this arrangement allows
207+
assignment of a new VXID at transaction start without any contention for
208+
shared memory. To ensure that a VXID isn't re-used too soon after backend
209+
exit, we store the last local counter value into shared memory at backend
210+
exit, and initialize it from the previous value for the same backendID slot
211+
at backend start. All these counters go back to zero at shared memory
212+
re-initialization, but that's OK because VXIDs never appear anywhere on-disk.
200213

201214
Internally, a backend needs a way to identify subtransactions whether or not
202215
they have XIDs; but this need only lasts as long as the parent top transaction
203216
endures. Therefore, we have SubTransactionId, which is somewhat like
204217
CommandId in that it's generated from a counter that we reset at the start of
205218
each top transaction. The top-level transaction itself has SubTransactionId 1,
206219
and subtransactions have IDs 2 and up. (Zero is reserved for
207-
InvalidSubTransactionId.)
220+
InvalidSubTransactionId.) Note that subtransactions do not have their
221+
own VXIDs; they use the parent top transaction's VXID.
208222

209223

210224
pg_clog and pg_subtrans

src/backend/access/transam/clog.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
2727
* Portions Copyright (c) 1994, Regents of the University of California
2828
*
29-
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.43 2007/08/01 22:45:07 tgl Exp $
29+
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.44 2007/09/05 18:10:47 tgl Exp $
3030
*
3131
*-------------------------------------------------------------------------
3232
*/
@@ -423,10 +423,6 @@ CLOGPagePrecedes(int page1, int page2)
423423

424424
/*
425425
* Write a ZEROPAGE xlog record
426-
*
427-
* Note: xlog record is marked as outside transaction control, since we
428-
* want it to be redone whether the invoking transaction commits or not.
429-
* (Besides which, this is normally done just before entering a transaction.)
430426
*/
431427
static void
432428
WriteZeroPageXlogRec(int pageno)
@@ -437,17 +433,14 @@ WriteZeroPageXlogRec(int pageno)
437433
rdata.len = sizeof(int);
438434
rdata.buffer = InvalidBuffer;
439435
rdata.next = NULL;
440-
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
436+
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE, &rdata);
441437
}
442438

443439
/*
444440
* Write a TRUNCATE xlog record
445441
*
446442
* We must flush the xlog record to disk before returning --- see notes
447443
* in TruncateCLOG().
448-
*
449-
* Note: xlog record is marked as outside transaction control, since we
450-
* want it to be redone whether the invoking transaction commits or not.
451444
*/
452445
static void
453446
WriteTruncateXlogRec(int pageno)
@@ -459,7 +452,7 @@ WriteTruncateXlogRec(int pageno)
459452
rdata.len = sizeof(int);
460453
rdata.buffer = InvalidBuffer;
461454
rdata.next = NULL;
462-
recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE | XLOG_NO_TRAN, &rdata);
455+
recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE, &rdata);
463456
XLogFlush(recptr);
464457
}
465458

src/backend/access/transam/multixact.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
4343
* Portions Copyright (c) 1994, Regents of the University of California
4444
*
45-
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.24 2007/08/01 22:45:07 tgl Exp $
45+
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.25 2007/09/05 18:10:47 tgl Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -1842,9 +1842,6 @@ MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
18421842
/*
18431843
* Write an xlog record reflecting the zeroing of either a MEMBERs or
18441844
* OFFSETs page (info shows which)
1845-
*
1846-
* Note: xlog record is marked as outside transaction control, since we
1847-
* want it to be redone whether the invoking transaction commits or not.
18481845
*/
18491846
static void
18501847
WriteMZeroPageXlogRec(int pageno, uint8 info)
@@ -1855,7 +1852,7 @@ WriteMZeroPageXlogRec(int pageno, uint8 info)
18551852
rdata.len = sizeof(int);
18561853
rdata.buffer = InvalidBuffer;
18571854
rdata.next = NULL;
1858-
(void) XLogInsert(RM_MULTIXACT_ID, info | XLOG_NO_TRAN, &rdata);
1855+
(void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);
18591856
}
18601857

18611858
/*

src/backend/access/transam/twophase.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.32 2007/08/01 22:45:07 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.33 2007/09/05 18:10:47 tgl Exp $
1111
*
1212
* NOTES
1313
* Each global transaction is associated with a global transaction
@@ -274,9 +274,11 @@ MarkAsPreparing(TransactionId xid, const char *gid,
274274
MemSet(&gxact->proc, 0, sizeof(PGPROC));
275275
SHMQueueElemInit(&(gxact->proc.links));
276276
gxact->proc.waitStatus = STATUS_OK;
277+
gxact->proc.lxid = InvalidLocalTransactionId;
277278
gxact->proc.xid = xid;
278279
gxact->proc.xmin = InvalidTransactionId;
279280
gxact->proc.pid = 0;
281+
gxact->proc.backendId = InvalidBackendId;
280282
gxact->proc.databaseId = databaseid;
281283
gxact->proc.roleId = owner;
282284
gxact->proc.inCommit = false;
@@ -813,8 +815,8 @@ StartPrepare(GlobalTransaction gxact)
813815
hdr.prepared_at = gxact->prepared_at;
814816
hdr.owner = gxact->owner;
815817
hdr.nsubxacts = xactGetCommittedChildren(&children);
816-
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
817-
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
818+
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels, NULL);
819+
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels, NULL);
818820
StrNCpy(hdr.gid, gxact->gid, GIDSIZE);
819821

820822
save_state_data(&hdr, sizeof(TwoPhaseFileHeader));
@@ -1702,9 +1704,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
17021704
}
17031705
rdata[lastrdata].next = NULL;
17041706

1705-
recptr = XLogInsert(RM_XACT_ID,
1706-
XLOG_XACT_COMMIT_PREPARED | XLOG_NO_TRAN,
1707-
rdata);
1707+
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT_PREPARED, rdata);
17081708

17091709
/*
17101710
* We don't currently try to sleep before flush here ... nor is there
@@ -1784,9 +1784,7 @@ RecordTransactionAbortPrepared(TransactionId xid,
17841784
}
17851785
rdata[lastrdata].next = NULL;
17861786

1787-
recptr = XLogInsert(RM_XACT_ID,
1788-
XLOG_XACT_ABORT_PREPARED | XLOG_NO_TRAN,
1789-
rdata);
1787+
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT_PREPARED, rdata);
17901788

17911789
/* Always flush, since we're about to remove the 2PC state file */
17921790
XLogFlush(recptr);

0 commit comments

Comments
 (0)