接上一篇:https://cloud.tencent.com/developer/article/2000659
快照生成在pg13 --> pg14中有重大升级,后面会有几篇文章详细分析这部分
GetSnapshotData
先看看函数说明的区别
相同的部分:
- xmin 最小正在运行
- xmax 最大的已经完成的事务 1
- xid 运行列表
10 11 12 13 14 15 16 17 18 19
s r r r s r s r r r
xmin = 11
xmax = 16 1 = 17
xid = [11, 12, 13, 15]
不同的部分:
- RecentGlobalXmin新版删除了
- RecentGlobalDataXmin新版删除了
Postgresql14新版流程分析
代码语言:javascript复制Snapshot
GetSnapshotData(Snapshot snapshot)
{
ProcArrayStruct *arrayP = procArray;
TransactionId *other_xids = ProcGlobal->xids;
TransactionId xmin;
TransactionId xmax;
...
// 拿读锁访ProcArray
LWLockAcquire(ProcArrayLock, LW_SHARED);
// 判断是否能复用快照,新增功能
if (GetSnapshotDataReuse(snapshot))
{
LWLockRelease(ProcArrayLock);
return snapshot;
}
// 现在已经提交的最大事务ID
latest_completed = ShmemVariableCache->latestCompletedXid;
// 获得当前事务ID
// PGPROC中保存了XID,为了降低CacheMiss会从ProcGlobal->xids中获取当前事务ID
// mypgxactoff是XID数组下标。
mypgxactoff = MyProc->pgxactoff;
myxid = other_xids[mypgxactoff];
Assert(myxid == MyProc->xid);
// 最老的frozenxid: minimum datfrozenxid
oldestxid = ShmemVariableCache->oldestXid;
curXactCompletionCount = ShmemVariableCache->xactCompletionCount;
/* xmax is always latestCompletedXid 1 */
xmax = XidFromFullTransactionId(latest_completed);
TransactionIdAdvance(xmax);
Assert(TransactionIdIsNormal(xmax));
/* initialize xmin calculation with xmax */
xmin = xmax;
/* take own xid into account, saves a check inside the loop */
if (TransactionIdIsNormal(myxid) && NormalTransactionIdPrecedes(myxid, xmin))
xmin = myxid;
snapshot->takenDuringRecovery = RecoveryInProgress();
if (!snapshot->takenDuringRecovery)
{
int numProcs = arrayP->numProcs;
TransactionId *xip = snapshot->xip;
int *pgprocnos = arrayP->pgprocnos;
XidCacheStatus *subxidStates = ProcGlobal->subxidStates;
uint8 *allStatusFlags = ProcGlobal->statusFlags;
/*
* First collect set of pgxactoff/xids that need to be included in the
* snapshot.
*/
for (int pgxactoff = 0; pgxactoff < numProcs; pgxactoff )
{
/* Fetch xid just once - see GetNewTransactionId */
TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
uint8 statusFlags;
Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
/*
* If the transaction has no XID assigned, we can skip it; it
* won't have sub-XIDs either.
*/
if (likely(xid == InvalidTransactionId))
continue;
/*
* We don't include our own XIDs (if any) in the snapshot. It
* needs to be includeded in the xmin computation, but we did so
* outside the loop.
*/
if (pgxactoff == mypgxactoff)
continue;
/*
* The only way we are able to get here with a non-normal xid is
* during bootstrap - with this backend using
* BootstrapTransactionId. But the above test should filter that
* out.
*/
Assert(TransactionIdIsNormal(xid));
/*
* If the XID is >= xmax, we can skip it; such transactions will
* be treated as running anyway (and any sub-XIDs will also be >=
* xmax).
*/
if (!NormalTransactionIdPrecedes(xid, xmax))
continue;
/*
* Skip over backends doing logical decoding which manages xmin
* separately (check below) and ones running LAZY VACUUM.
*/
statusFlags = allStatusFlags[pgxactoff];
if (statusFlags & (PROC_IN_LOGICAL_DECODING | PROC_IN_VACUUM))
continue;
if (NormalTransactionIdPrecedes(xid, xmin))
xmin = xid;
/* Add XID to snapshot. */
xip[count ] = xid;
/*
* Save subtransaction XIDs if possible (if we've already
* overflowed, there's no point). Note that the subxact XIDs must
* be later than their parent, so no need to check them against
* xmin. We could filter against xmax, but it seems better not to
* do that much work while holding the ProcArrayLock.
*
* The other backend can add more subxids concurrently, but cannot
* remove any. Hence it's important to fetch nxids just once.
* Should be safe to use memcpy, though. (We needn't worry about
* missing any xids added concurrently, because they must postdate
* xmax.)
*
* Again, our own XIDs are not included in the snapshot.
*/
if (!suboverflowed)
{
if (subxidStates[pgxactoff].overflowed)
suboverflowed = true;
else
{
int nsubxids = subxidStates[pgxactoff].count;
if (nsubxids > 0)
{
int pgprocno = pgprocnos[pgxactoff];
PGPROC *proc = &allProcs[pgprocno];
pg_read_barrier(); /* pairs with GetNewTransactionId */
memcpy(snapshot->subxip subcount,
(void *) proc->subxids.xids,
nsubxids * sizeof(TransactionId));
subcount = nsubxids;
}
}
}
}
}
else
{
/*
* We're in hot standby, so get XIDs from KnownAssignedXids.
*
* We store all xids directly into subxip[]. Here's why:
*
* In recovery we don't know which xids are top-level and which are
* subxacts, a design choice that greatly simplifies xid processing.
*
* It seems like we would want to try to put xids into xip[] only, but
* that is fairly small. We would either need to make that bigger or
* to increase the rate at which we WAL-log xid assignment; neither is
* an appealing choice.
*
* We could try to store xids into xip[] first and then into subxip[]
* if there are too many xids. That only works if the snapshot doesn't
* overflow because we do not search subxip[] in that case. A simpler
* way is to just store all xids in the subxact array because this is
* by far the bigger array. We just leave the xip array empty.
*
* Either way we need to change the way XidInMVCCSnapshot() works
* depending upon when the snapshot was taken, or change normal
* snapshot processing so it matches.
*
* Note: It is possible for recovery to end before we finish taking
* the snapshot, and for newly assigned transaction ids to be added to
* the ProcArray. xmax cannot change while we hold ProcArrayLock, so
* those newly added transaction ids would be filtered away, so we
* need not be concerned about them.
*/
subcount = KnownAssignedXidsGetAndSetXmin(snapshot->subxip, &xmin,
xmax);
if (TransactionIdPrecedesOrEquals(xmin, procArray->lastOverflowedXid))
suboverflowed = true;
}
/*
* Fetch into local variable while ProcArrayLock is held - the
* LWLockRelease below is a barrier, ensuring this happens inside the
* lock.
*/
replication_slot_xmin = procArray->replication_slot_xmin;
replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
if (!TransactionIdIsValid(MyProc->xmin))
MyProc->xmin = TransactionXmin = xmin;
LWLockRelease(ProcArrayLock);
/* maintain state for GlobalVis* */
{
TransactionId def_vis_xid;
TransactionId def_vis_xid_data;
FullTransactionId def_vis_fxid;
FullTransactionId def_vis_fxid_data;
FullTransactionId oldestfxid;
/*
* Converting oldestXid is only safe when xid horizon cannot advance,
* i.e. holding locks. While we don't hold the lock anymore, all the
* necessary data has been gathered with lock held.
*/
oldestfxid = FullXidRelativeTo(latest_completed, oldestxid);
/* apply vacuum_defer_cleanup_age */
def_vis_xid_data =
TransactionIdRetreatedBy(xmin, vacuum_defer_cleanup_age);
/* Check whether there's a replication slot requiring an older xmin. */
def_vis_xid_data =
TransactionIdOlder(def_vis_xid_data, replication_slot_xmin);
/*
* Rows in non-shared, non-catalog tables possibly could be vacuumed
* if older than this xid.
*/
def_vis_xid = def_vis_xid_data;
/*
* Check whether there's a replication slot requiring an older catalog
* xmin.
*/
def_vis_xid =
TransactionIdOlder(replication_slot_catalog_xmin, def_vis_xid);
def_vis_fxid = FullXidRelativeTo(latest_completed, def_vis_xid);
def_vis_fxid_data = FullXidRelativeTo(latest_completed, def_vis_xid_data);
/*
* Check if we can increase upper bound. As a previous
* GlobalVisUpdate() might have computed more aggressive values, don't
* overwrite them if so.
*/
GlobalVisSharedRels.definitely_needed =
FullTransactionIdNewer(def_vis_fxid,
GlobalVisSharedRels.definitely_needed);
GlobalVisCatalogRels.definitely_needed =
FullTransactionIdNewer(def_vis_fxid,
GlobalVisCatalogRels.definitely_needed);
GlobalVisDataRels.definitely_needed =
FullTransactionIdNewer(def_vis_fxid_data,
GlobalVisDataRels.definitely_needed);
/* See temp_oldest_nonremovable computation in ComputeXidHorizons() */
if (TransactionIdIsNormal(myxid))
GlobalVisTempRels.definitely_needed =
FullXidRelativeTo(latest_completed, myxid);
else
{
GlobalVisTempRels.definitely_needed = latest_completed;
FullTransactionIdAdvance(&GlobalVisTempRels.definitely_needed);
}
/*
* Check if we know that we can initialize or increase the lower
* bound. Currently the only cheap way to do so is to use
* ShmemVariableCache->oldestXid as input.
*
* We should definitely be able to do better. We could e.g. put a
* global lower bound value into ShmemVariableCache.
*/
GlobalVisSharedRels.maybe_needed =
FullTransactionIdNewer(GlobalVisSharedRels.maybe_needed,
oldestfxid);
GlobalVisCatalogRels.maybe_needed =
FullTransactionIdNewer(GlobalVisCatalogRels.maybe_needed,
oldestfxid);
GlobalVisDataRels.maybe_needed =
FullTransactionIdNewer(GlobalVisDataRels.maybe_needed,
oldestfxid);
/* accurate value known */
GlobalVisTempRels.maybe_needed = GlobalVisTempRels.definitely_needed;
}
RecentXmin = xmin;
Assert(TransactionIdPrecedesOrEquals(TransactionXmin, RecentXmin));
snapshot->xmin = xmin;
snapshot->xmax = xmax;
snapshot->xcnt = count;
snapshot->subxcnt = subcount;
snapshot->suboverflowed = suboverflowed;
snapshot->snapXactCompletionCount = curXactCompletionCount;
snapshot->curcid = GetCurrentCommandId(false);
/*
* This is a new snapshot, so set both refcounts are zero, and mark it as
* not copied in persistent memory.
*/
snapshot->active_count = 0;
snapshot->regd_count = 0;
snapshot->copied = false;
GetSnapshotDataInitOldSnapshot(snapshot);
return snapshot;
}