mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +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/storage/ipc/sinvaladt.c,v 1.63 2007/01/05 22:19:38 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.64 2007/09/05 18:10:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,12 +19,15 @@
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/lwlock.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/sinvaladt.h"
|
||||
|
||||
|
||||
SISeg *shmInvalBuffer;
|
||||
|
||||
static LocalTransactionId nextLocalTransactionId;
|
||||
|
||||
static void CleanupInvalidationState(int status, Datum arg);
|
||||
static void SISetProcStateInvalid(SISeg *segP);
|
||||
|
||||
@@ -40,6 +43,8 @@ SInvalShmemSize(void)
|
||||
size = offsetof(SISeg, procState);
|
||||
size = add_size(size, mul_size(sizeof(ProcState), MaxBackends));
|
||||
|
||||
size = add_size(size, mul_size(sizeof(LocalTransactionId), MaxBackends));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -51,15 +56,21 @@ void
|
||||
SIBufferInit(void)
|
||||
{
|
||||
SISeg *segP;
|
||||
Size size;
|
||||
int i;
|
||||
bool found;
|
||||
|
||||
/* Allocate space in shared memory */
|
||||
size = offsetof(SISeg, procState);
|
||||
size = add_size(size, mul_size(sizeof(ProcState), MaxBackends));
|
||||
|
||||
shmInvalBuffer = segP = (SISeg *)
|
||||
ShmemInitStruct("shmInvalBuffer", SInvalShmemSize(), &found);
|
||||
ShmemInitStruct("shmInvalBuffer", size, &found);
|
||||
if (found)
|
||||
return;
|
||||
|
||||
segP->nextLXID = ShmemAlloc(sizeof(LocalTransactionId) * MaxBackends);
|
||||
|
||||
/* Clear message counters, save size of procState array */
|
||||
segP->minMsgNum = 0;
|
||||
segP->maxMsgNum = 0;
|
||||
@@ -69,11 +80,12 @@ SIBufferInit(void)
|
||||
|
||||
/* The buffer[] array is initially all unused, so we need not fill it */
|
||||
|
||||
/* Mark all backends inactive */
|
||||
/* Mark all backends inactive, and initialize nextLXID */
|
||||
for (i = 0; i < segP->maxBackends; i++)
|
||||
{
|
||||
segP->procState[i].nextMsgNum = -1; /* inactive */
|
||||
segP->procState[i].resetState = false;
|
||||
segP->nextLXID[i] = InvalidLocalTransactionId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +140,15 @@ SIBackendInit(SISeg *segP)
|
||||
elog(DEBUG2, "my backend id is %d", MyBackendId);
|
||||
#endif /* INVALIDDEBUG */
|
||||
|
||||
/* Advertise assigned backend ID in MyProc */
|
||||
MyProc->backendId = MyBackendId;
|
||||
|
||||
/* Reduce free slot count */
|
||||
segP->freeBackends--;
|
||||
|
||||
/* Fetch next local transaction ID into local memory */
|
||||
nextLocalTransactionId = segP->nextLXID[MyBackendId - 1];
|
||||
|
||||
/* mark myself active, with all extant messages already read */
|
||||
stateP->nextMsgNum = segP->maxMsgNum;
|
||||
stateP->resetState = false;
|
||||
@@ -160,6 +178,9 @@ CleanupInvalidationState(int status, Datum arg)
|
||||
|
||||
LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
|
||||
|
||||
/* Update next local transaction ID for next holder of this backendID */
|
||||
segP->nextLXID[MyBackendId - 1] = nextLocalTransactionId;
|
||||
|
||||
/* Mark myself inactive */
|
||||
segP->procState[MyBackendId - 1].nextMsgNum = -1;
|
||||
segP->procState[MyBackendId - 1].resetState = false;
|
||||
@@ -352,3 +373,30 @@ SIDelExpiredDataEntries(SISeg *segP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetNextLocalTransactionId --- allocate a new LocalTransactionId
|
||||
*
|
||||
* We split VirtualTransactionIds into two parts so that it is possible
|
||||
* to allocate a new one without any contention for shared memory, except
|
||||
* for a bit of additional overhead during backend startup/shutdown.
|
||||
* The high-order part of a VirtualTransactionId is a BackendId, and the
|
||||
* low-order part is a LocalTransactionId, which we assign from a local
|
||||
* counter. To avoid the risk of a VirtualTransactionId being reused
|
||||
* within a short interval, successive procs occupying the same backend ID
|
||||
* slot should use a consecutive sequence of local IDs, which is implemented
|
||||
* by copying nextLocalTransactionId as seen above.
|
||||
*/
|
||||
LocalTransactionId
|
||||
GetNextLocalTransactionId(void)
|
||||
{
|
||||
LocalTransactionId result;
|
||||
|
||||
/* loop to avoid returning InvalidLocalTransactionId at wraparound */
|
||||
do {
|
||||
result = nextLocalTransactionId++;
|
||||
} while (!LocalTransactionIdIsValid(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user