PostgreSQL 14在MVCC上面有所增强,以保证在大批量客户端连接场景下性能不会出现显著下降(详细可参见参考资料中的链接),本节对函数GetSnapshotData进行跟踪分析。
一、数据结构
ProcArrayStruct
/* Our shared memory area */
typedef struct ProcArrayStruct
{
//有效的procs个数
int numProcs; /* number of valid procs entries */
//分配的procs数组大小
int maxProcs; /* allocated size of procs array */
/*
* Known assigned XIDs handling
* 已知已分配的XIDs
*/
int maxKnownAssignedXids; /* allocated size of array ,分配的数组大小*/
int numKnownAssignedXids; /* current # of valid entries,当前第几个entry */
int tailKnownAssignedXids; /* index of oldest valid element,最旧有效元素的索引编号,在数组的尾部 */
int headKnownAssignedXids; /* index of newest element, + 1,最新元素编号+1,在数组的头部 */
slock_t known_assigned_xids_lck; /* protects head/tail pointers,用于保护head/tail指针的锁 */
/*
* Highest subxid that has been removed from KnownAssignedXids array to
* prevent overflow; or InvalidTransactionId if none. We track this for
* similar reasons to tracking overflowing cached subxids in PGPROC
* entries. Must hold exclusive ProcArrayLock to change this, and shared
* lock to read it.
* 从KnownAssignedXids中移除用于避免溢出的的subxid最大值,如无,则为InvalidTransactionId
* 跟踪该值的理由与跟踪溢出PGPROC中的subxids类似。
* 在修改该值前必须持有ProcArrayLock独占锁,读取则需要共享锁。
*/
TransactionId lastOverflowedXid;
/* oldest xmin of any replication slot */
/* 复制槽中最小的xmin */
TransactionId replication_slot_xmin;
/* oldest catalog xmin of any replication slot */
/* 复制槽中最小的字典xmin */
TransactionId replication_slot_catalog_xmin;
/* indexes into allProcs[], has PROCARRAY_MAXPROCS entries */
/* allProcs索引,个数 = PROCARRAY_MAXPROCS */
int pgprocnos[FLEXIBLE_ARRAY_MEMBER];
} ProcArrayStruct;
PROC_HDR
/*
* There is one ProcGlobal struct for the whole database cluster.
* 全局的ProcGlobal(PROC_HDR)结构体
*
* Adding/Removing an entry into the procarray requires holding *both*
* ProcArrayLock and XidGenLock in exclusive mode (in that order). Both are
* needed because the dense arrays (see below) are accessed from
* GetNewTransactionId() and GetSnapshotData(), and we don't want to add
* further contention by both using the same lock. Adding/Removing a procarray
* entry is much less frequent.
* 对proc数组的插入和删除,需要以独占的模式持有ProcArrayLock和XidGenLock,
* 原因是GetNewTransactionId()和GetSnapshotData()函数需要访问,并且我们不希望使用同样的锁来增加争用.
* 其实,插入和删除proc数组并不频繁.
*
* Some fields in PGPROC are mirrored into more densely packed arrays (e.g.
* xids), with one entry for each backend. These arrays only contain entries
* for PGPROCs that have been added to the shared array with ProcArrayAdd()
* (in contrast to PGPROC array which has unused PGPROCs interspersed).
* PGPROC中的一些字段镜像到更稠密的数组中(如xids),每一个后台进程都有一项.
* 这些数组只包含使用ProcArrayAdd()加入到共享数组的PGPROCs.
*
* The dense arrays are indexed by PGPROC->pgxactoff. Any concurrent
* ProcArrayAdd() / ProcArrayRemove() can lead to pgxactoff of a procarray
* member to change. Therefore it is only safe to use PGPROC->pgxactoff to
* access the dense array while holding either ProcArrayLock or XidGenLock.
* 稠密数组通过PGPROC->pgxactoff字段进行索引,所有同步ProcArrayAdd和ProcArrayRemove操作
* 会改变procarray中的pgxactoff成员变量.
*
* As long as a PGPROC is in the procarray, the mirrored values need to be
* maintained in both places in a coherent manner.
* 由于PGPROC在proc数组中,镜像值需要以一致的方式进行运维.
*
* The denser separate arrays are beneficial for three main reasons: First, to
* allow for as tight loops accessing the data as possible. Second, to prevent
* updates of frequently changing data (e.g. xmin) from invalidating
* cachelines also containing less frequently changing data (e.g. xid,
* statusFlags). Third to condense frequently accessed data into as few
* cachelines as possible.
* 基于以下3点原因,更稠密单独的数组会带来不少好处.
* 1.紧密的循环访问数据成为可能;
* 2.避免了对修改频度不高的数据进行修改导致CPU缓存无效的情况出现;
* 3.使频繁访问的数据放在小量的CPU缓存中成为可能.
*
* There are two main reasons to have the data mirrored between these dense
* arrays and PGPROC. First, as explained above, a PGPROC's array entries can
* only be accessed with either ProcArrayLock or XidGenLock held, whereas the
* PGPROC entries do not require that (obviously there may still be locking
* requirements around the individual field, separate from the concerns
* here). That is particularly important for a backend to efficiently checks
* it own values, which it often can safely do without locking. Second, the
* PGPROC fields allow to avoid unnecessary accesses and modification to the
* dense arrays. A backend's own PGPROC is more likely to be in a local cache,
* whereas the cachelines for the dense array will be modified by other
* backends (often removing it from the cache for other cores/sockets). At
* commit/abort time a check of the PGPROC value can avoid accessing/dirtying
* the corresponding array value.
* 让这些稠密数组和PGPROC之间生成数据镜像,有两个主要原因:
* 1.如上所述,PGPROC数组条目需要在持有ProcArrayLock和XidGenLock的前提下才能访问,但实际上并不需要.
* 在没有锁的情况下,后台进程有效且安全的检查自己的值是十分重要的.
* 2.PGPROC中的字段避免不必要的访问和修改.后台进程自己的PGPROC更应该在自己的本地缓存中,
* 而无需理会数组会被其他进程修改.在事务提交或回滚时,检查PGPROC值可以避免访问或者弄脏相应的数组值
*
* Basically it makes sense to access the PGPROC variable when checking a
* single backend's data, especially when already looking at the PGPROC for
* other reasons already. It makes sense to look at the "dense" arrays if we
* need to look at many / most entries, because we then benefit from the
* reduced indirection and better cross-process cache-ability.
* 检查单个后台进程数据时,访问PGPROC值是比较敏感的,特别是在已经因为其他原因检索过PGPROC的情况下
*
* When entering a PGPROC for 2PC transactions with ProcArrayAdd(), the data
* in the dense arrays is initialized from the PGPROC while it already holds
* ProcArrayLock.
* 在2PC事务的情况下,通过ProcArrayAdd方法进入PGPROC时,
* 数组中的数据在持有ProcArrayLock锁的情况下初始化数组.
*/
typedef struct PROC_HDR
{
/* Array of PGPROC structures (not including dummies for prepared txns) */
//PGPROC结构体数组
PGPROC *allProcs;
/* Array mirroring PGPROC.xid for each PGPROC currently in the procarray */
// 镜像每一个PGPROC.xid
TransactionId *xids;
/*
* Array mirroring PGPROC.subxidStatus for each PGPROC currently in the
* procarray.
*/
//镜像PGPROC.subxidStatus
XidCacheStatus *subxidStates;
/*
* Array mirroring PGPROC.statusFlags for each PGPROC currently in the
* procarray.
*/
//镜像PGPROC.statusFlags
uint8 *statusFlags;
/* Length of allProcs array */
//allProcs数组的大小
uint32 allProcCount;
/* Head of list of free PGPROC structures */
//空闲PGPROC结构体链表头
PGPROC *freeProcs;
/* Head of list of autovacuum's free PGPROC structures */
//autovacuum进程空闲PGPROC结构体链表头
PGPROC *autovacFreeProcs;
/* Head of list of bgworker free PGPROC structures */
//bgworker进程
PGPROC *bgworkerFreeProcs;
/* Head of list of walsender free PGPROC structures */
//walsender
PGPROC *walsenderFreeProcs;
/* First pgproc waiting for group XID clear */
//等待XID组清理的第一个pgproc
pg_atomic_uint32 procArrayGroupFirst;
/* First pgproc waiting for group transaction status update */
//等待事务状态组更新的首个pgproc
pg_atomic_uint32 clogGroupFirst;
/* WALWriter process's latch */
//WALWriter进程latch
Latch *walwriterLatch;
/* Checkpointer process's latch */
//检查点进程latch
Latch *checkpointerLatch;
/* Current shared estimate of appropriate spins_per_delay value */
//spins_per_delay
int spins_per_delay;
/* The proc of the Startup process, since not in ProcArray */
//Startup进程的proc结构体,不再Proc数组中
PGPROC *startupProc;
int startupProcPid;
/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
//Startup进程等待pin的缓存ID
int startupBufferPinWaitBufId;
} PROC_HDR;
extern PGDLLIMPORT PROC_HDR *ProcGlobal;
PGPROC
/*
* Note: MAX_BACKENDS is limited to 2^18-1 because that's the width reserved
* for buffer references in buf_internals.h. This limitation could be lifted
* by using a 64bit state; but it's unlikely to be worthwhile as 2^18-1
* backends exceed currently realistic configurations. Even if that limitation
* were removed, we still could not a) exceed 2^23-1 because inval.c stores
* the backend ID as a 3-byte signed integer, b) INT_MAX/4 because some places
* compute 4*MaxBackends without any overflow check. This is rechecked in the
* relevant GUC check hooks and in RegisterBackgroundWorker().
* 注意:MAX_BACKENDS限制为2^18-1,
* 这是因为该值为buf_internals.h中定义的缓存依赖的最大宽度.
* 该限制可以通过使用64bit的状态来提升,但它看起来并不值当.
* 如果去掉该限制,我们仍然不能够超过:
* a) 2^23-1,因为inval.c使用3个字节的有符号整数存储后台进程ID
* b) INT_MAX/4 ,因为某些地方没有任何的溢出检查,直接计算4*MaxBackends的值.
* 该值会在相关的GUC检查钩子和RegisterBackgroundWorker()函数中检查.
*/
#define MAX_BACKENDS 0x3FFFF
/* shmqueue.c */
typedef struct SHM_QUEUE
{
struct SHM_QUEUE *prev;
struct SHM_QUEUE *next;
} SHM_QUEUE;
/*
* An invalid pgprocno. Must be larger than the maximum number of PGPROC
* structures we could possibly have. See comments for MAX_BACKENDS.
* 无效的pg进程号.
* 必须大于我们可能拥有的最大的PGPROC数目.
* 详细解释见MAX_BACKENDS
*/
#define INVALID_PGPROCNO PG_INT32_MAX
/*
* Each backend has a PGPROC struct in shared memory. There is also a list of
* currently-unused PGPROC structs that will be reallocated to new backends.
* 每个后台进程在共享内存中都有一个PGPROC结构体.
* 存在未使用的PGPROC结构体链表,用于为新的后台进程重新进行分配.
*
* links: list link for any list the PGPROC is in. When waiting for a lock,
* the PGPROC is linked into that lock's waitProcs queue. A recycled PGPROC
* is linked into ProcGlobal's freeProcs list.
* links: PGPROC所在的链表的链接.
* 在等待锁时,PGPROC链接到该锁的waiProc队列中.
* 回收的PGPROC链接到ProcGlobal的freeProcs链表中.
*
* Note: twophase.c also sets up a dummy PGPROC struct for each currently
* prepared transaction. These PGPROCs appear in the ProcArray data structure
* so that the prepared transactions appear to be still running and are
* correctly shown as holding locks. A prepared transaction PGPROC can be
* distinguished from a real one at need by the fact that it has pid == 0.
* The semaphore and lock-activity fields in a prepared-xact PGPROC are unused,
* but its myProcLocks[] lists are valid.
* 注意:twophase.c也会为每一个当前已准备妥当的事务配置一个虚拟的PGPROC结构.
* 这些PGPROCs在数组ProcArray数据结构中出现,以便已准备的事务看起来仍在运行,
* 并正确的显示为持有锁.
* 已准备妥当的事务PGPROC与一个真正的PGPROC事实上的区别是pid == 0.
* 在prepared-xact PGPROC中的信号量和活动锁域字段没有使用,但myProcLocks[]链表是有效的.
*/
struct PGPROC
{
/* proc->links MUST BE FIRST IN STRUCT (see ProcSleep,ProcWakeup,etc) */
//proc->links必须是结构体的第一个域(参考ProcSleep,ProcWakeup...等)
//如进程在链表中,这是链表的链接
SHM_QUEUE links; /* list link if process is in a list */
//持有该PGPROC的procglobal链表数组
PGPROC **procgloballist; /* procglobal list that owns this PGPROC */
//可以休眠的信号量
PGSemaphore sem; /* ONE semaphore to sleep on */
//状态为:STATUS_WAITING, STATUS_OK or STATUS_ERROR
int waitStatus; /* STATUS_WAITING, STATUS_OK or STATUS_ERROR */
//进程通用的latch
Latch procLatch; /* generic latch for process */
//运行中的进程正在执行的最高层的事务本地ID,如无运行则为InvalidLocalTransactionId
LocalTransactionId lxid; /* local id of top-level transaction currently
* being executed by this proc, if running;
* else InvalidLocalTransactionId */
//后台进程的ID,如为虚拟事务则为0
int pid; /* Backend's process ID; 0 if prepared xact */
int pgprocno;
/* These fields are zero while a backend is still starting up: */
//------------ 这些域在进程正在启动时为0
//已分配的后台进程的backend ID
BackendId backendId; /* This backend's backend ID (if assigned) */
//该进程使用的数据库ID
Oid databaseId; /* OID of database this backend is using */
//使用该进程的角色ID
Oid roleId; /* OID of role using this backend */
//该进程使用的临时schema OID
Oid tempNamespaceId; /* OID of temp schema this backend is
* using */
//如后台进程,则为T
bool isBackgroundWorker; /* true if background worker. */
/*
* While in hot standby mode, shows that a conflict signal has been sent
* for the current transaction. Set/cleared while holding ProcArrayLock,
* though not required. Accessed without lock, if needed.
* 如在hot standby模式,显示已为当前事务发送冲突信号.
* 尽管不需要,设置/清除持有的ProcArrayLock.
* 如需要,则在没有持有锁的情况下访问.
*/
bool recoveryConflictPending;
/* Info about LWLock the process is currently waiting for, if any. */
//-------------- 进程正在等待的LWLock相关信息
//等待LW lock,为T
bool lwWaiting; /* true if waiting for an LW lock */
//正在等的LWLock锁模式
uint8 lwWaitMode; /* lwlock mode being waited for */
//等待链表中的位置
proclist_node lwWaitLink; /* position in LW lock wait list */
/* Support for condition variables. */
//-------------- 支持条件变量
//CV等待链表中的位置
proclist_node cvWaitLink; /* position in CV wait list */
/* Info about lock the process is currently waiting for, if any. */
//-------------- 进程正在等待的锁信息
/* waitLock and waitProcLock are NULL if not currently waiting. */
//如没有在等待,则waitLock和waitProcLock为NULL
//休眠...等待的锁对象
LOCK *waitLock; /* Lock object we're sleeping on ... */
//等待锁的每个持锁人信息
PROCLOCK *waitProcLock; /* Per-holder info for awaited lock */
//等待的所类型
LOCKMODE waitLockMode; /* type of lock we're waiting for */
//该进程已持有锁的类型位掩码
LOCKMASK heldLocks; /* bitmask for lock types already held on this
* lock object by this backend */
/*
* Info to allow us to wait for synchronous replication, if needed.
* waitLSN is InvalidXLogRecPtr if not waiting; set only by user backend.
* syncRepState must not be touched except by owning process or WALSender.
* syncRepLinks used only while holding SyncRepLock.
* 允许我们等待同步复制的相关信息.
* 如无需等待,则waitLSN为InvalidXLogRecPtr;仅允许由用户后台设置。
* 除非拥有process或WALSender,否则不能修改syncRepState。
* 仅在持有SyncRepLock时使用的syncrepink。
*/
//---------------------
//等待该LSN或者更高的LSN
XLogRecPtr waitLSN; /* waiting for this LSN or higher */
//同步复制的等待状态
int syncRepState; /* wait state for sync rep */
//如进程处于syncrep队列中,则该值保存链表链接
SHM_QUEUE syncRepLinks; /* list link if process is in syncrep queue */
/*
* All PROCLOCK objects for locks held or awaited by this backend are
* linked into one of these lists, according to the partition number of
* their lock.
* 该后台进程持有或等待的锁相关的所有PROCLOCK对象链接在这些链表的末尾,
* 根据棣属于这些锁的分区号进行区分.
*/
SHM_QUEUE myProcLocks[NUM_LOCK_PARTITIONS];
//子事务的XIDs
struct XidCache subxids; /* cache for subtransaction XIDs */
/* Support for group XID clearing. */
/* true, if member of ProcArray group waiting for XID clear */
//支持XID分组清除
//如属于等待XID清理的ProcArray组,则为T
bool procArrayGroupMember;
/* next ProcArray group member waiting for XID clear */
//等待XID清理的下一个ProcArray组编号
pg_atomic_uint32 procArrayGroupNext;
/*
* latest transaction id among the transaction's main XID and
* subtransactions
* 在事务主XID和子事务之间的最后的事务ID
*/
TransactionId procArrayGroupMemberXid;
//进程的等待信息
uint32 wait_event_info; /* proc's wait information */
/* Support for group transaction status update. */
//--------------- 支持组事务状态更新
//clog组成员,则为T
bool clogGroupMember; /* true, if member of clog group */
//下一个clog组成员
pg_atomic_uint32 clogGroupNext; /* next clog group member */
//clog组成员事务ID
TransactionId clogGroupMemberXid; /* transaction id of clog group member */
//clog组成员的事务状态
XidStatus clogGroupMemberXidStatus; /* transaction status of clog
* group member */
//属于clog组成员的事务ID的clog page
int clogGroupMemberPage; /* clog page corresponding to
* transaction id of clog group member */
//clog组成员已提交记录的WAL位置
XLogRecPtr clogGroupMemberLsn; /* WAL location of commit record for clog
* group member */
/* Per-backend LWLock. Protects fields below (but not group fields). */
//每一个后台进程一个LWLock.保护下面的域字段(非组字段)
LWLock backendLock;
/* Lock manager data, recording fast-path locks taken by this backend. */
//---------- 锁管理数据,记录该后台进程以最快路径获得的锁
//每一个fast-path slot的锁模式
uint64 fpLockBits; /* lock modes held for each fast-path slot */
//rel oids的slots
Oid fpRelId[FP_LOCK_SLOTS_PER_BACKEND]; /* slots for rel oids */
//是否持有fast-path VXID锁
bool fpVXIDLock; /* are we holding a fast-path VXID lock? */
//fast-path VXID锁的lxid
LocalTransactionId fpLocalTransactionId; /* lxid for fast-path VXID
* lock */
/*
* Support for lock groups. Use LockHashPartitionLockByProc on the group
* leader to get the LWLock protecting these fields.
*/
//--------- 支持锁组.
// 在组leader中使用LockHashPartitionLockByProc获取LWLock保护这些域
//锁组的leader,如果"我"是其中一员
PGPROC *lockGroupLeader; /* lock group leader, if I'm a member */
//如果"我"是leader,这是成员的链表
dlist_head lockGroupMembers; /* list of members, if I'm a leader */
//成员连接,如果"我"是其中一员
dlist_node lockGroupLink; /* my member link, if I'm a member */
};
/* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
//注意:"typedef struct PGPROC PGPROC"在storage/lock.h中出现
extern PGDLLIMPORT PGPROC *MyProc;
extern PGDLLIMPORT struct PGXACT *MyPgXact;
SnapshotData
/*
* The different snapshot types. We use SnapshotData structures to represent
* both "regular" (MVCC) snapshots and "special" snapshots that have non-MVCC
* semantics. The specific semantics of a snapshot are encoded by its type.
* 快照类型。
* 使用SnapshotData结构体表示常规的(MVCC)的快照和特别的快照(非MVCC语义)。
* 快照的特殊语义通过其类型进行编码。
*
* The behaviour of each type of snapshot should be documented alongside its
* enum value, best in terms that are not specific to an individual table AM.
* 每种快照类型的行为与其enum值一起记录而不是特定于单个表的AM。
*
* The reason the snapshot type rather than a callback as it used to be is
* that that allows to use the same snapshot for different table AMs without
* having one callback per AM.
* 使用快照类型而不是回调函数的原因是对于不同的表AMs(访问方法)可使用相同的快照而不是每个AM使用一个回调函数
*/
typedef enum SnapshotType
{
/*-------------------------------------------------------------------------
* A tuple is visible iff the tuple is valid for the given MVCC snapshot.
*
* Here, we consider the effects of:
* - all transactions committed as of the time of the given snapshot
* - previous commands of this transaction
*
* Does _not_ include:
* - transactions shown as in-progress by the snapshot
* - transactions started after the snapshot was taken
* - changes made by the current command
* 元组对于给定的MVCC快照是有效的,则该元组可见
* 考虑:
* 给定快照时所有提交的事务
* 该事务先前执行的命令
* 不考虑:
* 正在处理中的事务
* 快照点之后的事务
* 当前命令做出的修改
* -------------------------------------------------------------------------
*/
SNAPSHOT_MVCC = 0,//常规的MVCC
/*-------------------------------------------------------------------------
* A tuple is visible iff the tuple is valid "for itself".
* 对于“自身”是有效,则元组可见
*
* Here, we consider the effects of:
* - all committed transactions (as of the current instant)
* - previous commands of this transaction
* - changes made by the current command
*
* Does _not_ include:
* - in-progress transactions (as of the current instant)
* 考虑:
* 所有已提交的事务(当前实例)
* 本事务先前的命令
* 当前命令的修改
* 不考虑
* 正在进行中的事务
* -------------------------------------------------------------------------
*/
SNAPSHOT_SELF,
/*
* Any tuple is visible.
* 所有元组都是可见的
*/
SNAPSHOT_ANY,
/*
* A tuple is visible iff the tuple is valid as a TOAST row.
* 元组作为TOAST行为有效时,元组是可见的。
*/
SNAPSHOT_TOAST,
/*-------------------------------------------------------------------------
* A tuple is visible iff the tuple is valid including effects of open
* transactions.
* 只要元组有效(包括开放事务的影响)则元组可见
*
* Here, we consider the effects of:
* - all committed and in-progress transactions (as of the current instant)
* - previous commands of this transaction
* - changes made by the current command
* 这里,考虑:
* 所有已提交和处理中的事务
* 该事务先前的命令
* 当前命令作出的修改
*
* This is essentially like SNAPSHOT_SELF as far as effects of the current
* transaction and committed/aborted xacts are concerned. However, it
* also includes the effects of other xacts still in progress.
* 就当前和提交/中止事务的影响来看,本质上类似于SNAPSHOT_SELF。
*
* A special hack is that when a snapshot of this type is used to
* determine tuple visibility, the passed-in snapshot struct is used as an
* output argument to return the xids of concurrent xacts that affected
* the tuple. snapshot->xmin is set to the tuple's xmin if that is
* another transaction that's still in progress; or to
* InvalidTransactionId if the tuple's xmin is committed good, committed
* dead, or my own xact. Similarly for snapshot->xmax and the tuple's
* xmax. If the tuple was inserted speculatively, meaning that the
* inserter might still back down on the insertion without aborting the
* whole transaction, the associated token is also returned in
* snapshot->speculativeToken. See also InitDirtySnapshot().
* 这种快照用于确定元组的可见性,传入的快照结构体用作输出参数返回当前影响元组的事务xids。
* snapshot->xmin设置为元组的xmin,如果这是另外一个进行中的事务;
* 设置为InvalidTransactionId如果元组的xmin对应的事务已完成提交/终止/或者是本事务
* snapshot->xmax与元组xmax与此类似。
* 如果tuple是speculatively插入的,这意味着插入事务仍可以继续插入,而不会中止整个事务,
* 相关的令牌会通过snapshot—>speculativeToken返回。
* -------------------------------------------------------------------------
*/
SNAPSHOT_DIRTY,
/*
* A tuple is visible iff it follows the rules of SNAPSHOT_MVCC, but
* supports being called in timetravel context (for decoding catalog
* contents in the context of logical decoding).
* 如遵循SNAPSHOT_MVCC规则,则元组是可见的,但支持在timetravel上下文中调用
* (在逻辑解码上下文时解码catalog内容)
*/
SNAPSHOT_HISTORIC_MVCC,
/*
* A tuple is visible iff the tuple might be visible to some transaction;
* false if it's surely dead to everyone, i.e., vacuumable.
*
* For visibility checks snapshot->min must have been set up with the xmin
* horizon to use.
* 对于某些事务是可见的,则元组可见。
* 如已确认“死亡”则为False,如需要被清理。
* 对于可见性检查而言,snapshot->min必须使用xmin来配置
*/
SNAPSHOT_NON_VACUUMABLE
} SnapshotType;
typedef struct SnapshotData *Snapshot;
#define InvalidSnapshot ((Snapshot) NULL)
/*
* Struct representing all kind of possible snapshots.
* 对应所有可能快照的结构体
*
* There are several different kinds of snapshots:
* * Normal MVCC snapshots
* * MVCC snapshots taken during recovery (in Hot-Standby mode)
* * Historic MVCC snapshots used during logical decoding
* * snapshots passed to HeapTupleSatisfiesDirty()
* * snapshots passed to HeapTupleSatisfiesNonVacuumable()
* * snapshots used for SatisfiesAny, Toast, Self where no members are
* accessed.
* 快照类型包括(详见SnapshotType):
* 普通的MVCC快照
* 恢复期间的MVCC快照
* 逻辑解码时的历史MVCC快照
* 传递给HeapTupleSatisfiesDirty的快照
* 传递给HeapTupleSatisfiesNonVacuumable的快照
* 传递给SatisfiesAny,Toast,Self的快照
*
* TODO: It's probably a good idea to split this struct using a NodeTag
* similar to how parser and executor nodes are handled, with one type for
* each different kind of snapshot to avoid overloading the meaning of
* individual fields.
* TODO:通过NodeTag来拆分结构体
*/
typedef struct SnapshotData
{
//快照类型
SnapshotType snapshot_type; /* type of snapshot */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
* just zeroes in special snapshots. (But xmin and xmax are used
* specially by HeapTupleSatisfiesDirty, and xmin is used specially by
* HeapTupleSatisfiesNonVacuumable.)
* 余下字段用于MVCC快照,其他特殊的快照设置为0.
* (xmin和xmax用于HeapTupleSatisfiesDirty,xmin用于HeapTupleSatisfiesNonVacuumable)
*
* An MVCC snapshot can never see the effects of XIDs >= xmax. It can see
* the effects of all older XIDs except those listed in the snapshot. xmin
* is stored as an optimization to avoid needing to search the XID arrays
* for most tuples.
* MVCC快照用于都看不到XIDs >= xmax的影响,但可以看到除了快照列表中的事务影响。
* xmin作为优化的手段用于避免检索XID数组
*/
TransactionId xmin; /* all XID < xmin are visible to me */
TransactionId xmax; /* all XID >= xmax are invisible to me */
/*
* For normal MVCC snapshot this contains the all xact IDs that are in
* progress, unless the snapshot was taken during recovery in which case
* it's empty. For historic MVCC snapshots, the meaning is inverted, i.e.
* it contains *committed* transactions between xmin and xmax.
* xip用于存放所有正在进行中的事务,除了在恢复期间的快照。
* 对于历史MVCC快照,意思的相反的,也就是说xip是xmin和xmax之间已提交的事务
*
* note: all ids in xip[] satisfy xmin <= xip[i] < xmax
* xip数组中的元素满足:xmin <= xip[i] < xmax
*/
TransactionId *xip;
uint32 xcnt; /* 数组大小,# of xact ids in xip[] */
/*
* For non-historic MVCC snapshots, this contains subxact IDs that are in
* progress (and other transactions that are in progress if taken during
* recovery). For historic snapshot it contains *all* xids assigned to the
* replayed transaction, including the toplevel xid.
* 对于非历史MVCC快照,包含正在进行中的子事务IDs。
* 对于历史MVCC快照,包含所有分配给已回放事务的所有xids(包括顶层xid)
*
* note: all ids in subxip[] are >= xmin, but we don't bother filtering
* out any that are >= xmax
*/
TransactionId *subxip;
int32 subxcnt; /* 数组大小,# of xact ids in subxip[] */
bool suboverflowed; /* 数组溢出?has the subxip array overflowed? */
bool takenDuringRecovery; /* recovery-shaped snapshot? */
bool copied; /* false if it's a static snapshot */
CommandId curcid; /* 在本事务中,CID < curcid则可见,in my xact, CID < curcid are visible */
/*
* An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
* snapshots.
* HeapTupleSatisfiesDirty额外的返回值
*/
uint32 speculativeToken;
/*
* For SNAPSHOT_NON_VACUUMABLE (and hopefully more in the future) this is
* used to determine whether row could be vacuumed.
* 对于SNAPSHOT_NON_VACUUMABLE,用于确定哪些行可以被清理
*/
struct GlobalVisState *vistest;
/*
* Book-keeping information, used by the snapshot manager
* 用于快照管理器的信息
*/
uint32 active_count; /* refcount on ActiveSnapshot stack */
uint32 regd_count; /* refcount on RegisteredSnapshots */
pairingheap_node ph_node; /* link in the RegisteredSnapshots heap */
TimestampTz whenTaken; /* timestamp when snapshot was taken */
XLogRecPtr lsn; /* position in the WAL stream when taken */
/*
* The transaction completion count at the time GetSnapshotData() built
* this snapshot. Allows to avoid re-computing static snapshots when no
* transactions completed since the last GetSnapshotData().
*/
uint64 snapXactCompletionCount;
} SnapshotData;
VariableCacheData
/*
* VariableCache is a data structure in shared memory that is used to track
* OID and XID assignment state. For largely historical reasons, there is
* just one struct with different fields that are protected by different
* LWLocks.
* VariableCache是共享内存中的一种数据结构,用于跟踪OID和XID分配状态。
* 由于历史原因,这个结构体有不同的字段,由不同的LWLocks保护。
*
* Note: xidWrapLimit and oldestXidDB are not "active" values, but are
* used just to generate useful messages when xidWarnLimit or xidStopLimit
* are exceeded.
* 注意:xidWrapLimit和oldestXidDB是不"活跃"的值,在xidWarnLimit或xidStopLimit
* 超出限制时用于产生有用的信息.
*/
typedef struct VariableCacheData
{
/*
* These fields are protected by OidGenLock.
* 这些域字段通过OidGenLock字段保护
*/
//下一个待分配的OID
Oid nextOid; /* next OID to assign */
//在必须执行XLOG work前可用OIDs
uint32 oidCount; /* OIDs available before must do XLOG work */
/*
* These fields are protected by XidGenLock.
* 这些字段通过XidGenLock锁保护.
*/
//下一个待分配的事务ID
TransactionId nextXid; /* next XID to assign */
//集群范围内最小datfrozenxid
TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */
//在该XID开始强制执行autovacuum
TransactionId xidVacLimit; /* start forcing autovacuums here */
//在该XID开始提出警告
TransactionId xidWarnLimit; /* start complaining here */
//在该XID开外,拒绝生成下一个XID
TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
//"世界末日"XID,需回卷
TransactionId xidWrapLimit; /* where the world ends */
//持有最小datfrozenxid的DB
Oid oldestXidDB; /* database with minimum datfrozenxid */
/*
* These fields are protected by CommitTsLock
* 这些字段通过CommitTsLock锁保护
*/
TransactionId oldestCommitTsXid;
TransactionId newestCommitTsXid;
/*
* These fields are protected by ProcArrayLock.
* 这些字段通过ProcArrayLock锁保护
*/
TransactionId latestCompletedXid; /* newest XID that has committed or
* aborted */
/*
* These fields are protected by CLogTruncationLock
* 这些字段通过CLogTruncationLock锁保护
*/
//clog中最古老的XID
TransactionId oldestClogXid; /* oldest it's safe to look up in clog */
} VariableCacheData;
//结构体指针
typedef VariableCacheData *VariableCache;
/* pointer to "variable cache" in shared memory (set up by shmem.c) */
//共享内存中的指针(通过shmem.c设置)
VariableCache ShmemVariableCache = NULL;
GlobalVisState
/*
* State for the GlobalVisTest* family of functions. Those functions can
* e.g. be used to decide if a deleted row can be removed without violating
* MVCC semantics: If the deleted row's xmax is not considered to be running
* by anyone, the row can be removed.
* 用于配合GlobalVisTest*函数族使用的数据结构。
* 这些函数用于确定删除的行在不违反MVCC语义的情况下是否可以被清除:
* 如果被删除的行的xmax与所有运行的事务无关,那么可以清除。
*
* To avoid slowing down GetSnapshotData(), we don't calculate a precise
* cutoff XID while building a snapshot (looking at the frequently changing
* xmins scales badly). Instead we compute two boundaries while building the
* snapshot:
*
* 1) definitely_needed, indicating that rows deleted by XIDs >=
* definitely_needed are definitely still visible.
*
* 2) maybe_needed, indicating that rows deleted by XIDs < maybe_needed can
* definitely be removed
*
* 为了避免影响GetSnapshotData()的性能,在构建快照时我们不会计算精确的cutoff XID,
* 相反,我们会在构建快照时计算两个边界,分别是:
* 1)definitely_needed,表示如果删除事务的XIDs >= definitely_needed,则可见
* 2)maybe_needed,表示被XIDs < maybe_needed 事务删除的,可以确定被清除
*
* When testing an XID that falls in between the two (i.e. XID >= maybe_needed
* && XID < definitely_needed), the boundaries can be recomputed (using
* ComputeXidHorizons()) to get a more accurate answer. This is cheaper than
* maintaining an accurate value all the time.
* 对于XID >= maybe_needed && XID < definitely_needed的XID,
* 边界需要使用函数ComputeXidHorizons重新计算来获取更精确的结果,这比在所有情况下都维护一个精确值成本要低
*
* As it is not cheap to compute accurate boundaries, we limit the number of
* times that happens in short succession. See GlobalVisTestShouldUpdate().
* 计算精确的边界成本高昂,因此需要限制短时间内出现的次数,详细参见GlobalVisTestShouldUpdate函数
*
* There are three backend lifetime instances of this struct, optimized for
* different types of relations. As e.g. a normal user defined table in one
* database is inaccessible to backends connected to another database, a test
* specific to a relation can be more aggressive than a test for a shared
* relation. Currently we track four different states:
* 对于不同的关系,出于优化的目的,该结构有三种不同的backend生命周期。
* 比如某个数据库中用户定义的普通表对于连接到另外一个数据库的backends是不可见的,
* 对某个特定关系的验证比起共享关系的验证要更为积极。我们跟踪四种不同的状态:
*
* 1) GlobalVisSharedRels, which only considers an XID's
* effects visible-to-everyone if neither snapshots in any database, nor a
* replication slot's xmin, nor a replication slot's catalog_xmin might
* still consider XID as running.
* 1)GlobalVisSharedRels,事务对该Relation的操作会影响所有数据库中的快照、复制槽的xmin和复制槽的catalog_xmin。
*
* 2) GlobalVisCatalogRels, which only considers an XID's
* effects visible-to-everyone if neither snapshots in the current
* database, nor a replication slot's xmin, nor a replication slot's
* catalog_xmin might still consider XID as running.
*
* I.e. the difference to GlobalVisSharedRels is that
* snapshot in other databases are ignored.
* 2)GlobalVisCatalogRels,事务对该Relation的操作会影响当前数据库中的快照、复制槽的xmin和复制槽的catalog_xmin
* 与GlobalVisSharedRels不同之处是其他数据库的快照会被忽略
*
* 3) GlobalVisDataRels, which only considers an XID's
* effects visible-to-everyone if neither snapshots in the current
* database, nor a replication slot's xmin consider XID as running.
*
* I.e. the difference to GlobalVisCatalogRels is that
* replication slot's catalog_xmin is not taken into account.
*
* 3)GlobalVisDataRels,事务对该Relation的操作会影响当前数据库中的快照、复制槽的xmin
* 与GlobalVisCatalogRels不同的是catalog_xmin不作考虑
*
* 4) GlobalVisTempRels, which only considers the current session, as temp
* tables are not visible to other sessions.
*
* 4)GlobalVisTempRels,只考虑当前会话,临时表对其他session不可见
*
* GlobalVisTestFor(relation) returns the appropriate state
* for the relation.
* GlobalVisTestFor(relation)返回关系正确的状态
*
* The boundaries are FullTransactionIds instead of TransactionIds to avoid
* wraparound dangers. There e.g. would otherwise exist no procarray state to
* prevent maybe_needed to become old enough after the GetSnapshotData()
* call.
* 边界使用FullTransactionIds而不是TransactionIds以避免因为回卷而导致的错误。
* 例如,在GetSnapshotData()调用后,将不存在procarray状态来防止maybe_needed变得32bit XID中存在的过老现象。
*
* The typedef is in the header.
*/
struct GlobalVisState
{
/* XIDs >= are considered running by some backend */
//大于等于该值的XIDs:其他活动backend可见
FullTransactionId definitely_needed;
/* XIDs < are not considered to be running by any backend */
//小于该值的XIDs:不再视为正在运行,均可见
FullTransactionId maybe_needed;
};
/*
* A 64 bit value that contains an epoch and a TransactionId. This is
* wrapped in a struct to prevent implicit conversion to/from TransactionId.
* Not all values represent valid normal XIDs.
* 号外:PG 14已支持64 bit XID,令人厌烦的XID回卷问题不复存在
*/
typedef struct FullTransactionId
{
uint64 value;
} FullTransactionId;
二、源码解读
N/A
三、跟踪分析
SESSION
-- session 1
[local:/opt/data5014]:5014 pgmaster@testdb=# begin;
BEGIN
Time: 0.370 ms
[local:/opt/data5014]:5014 pgmaster@testdb=#* insert into t1 values(1);
INSERT 0 1
Time: 0.805 ms
[local:/opt/data5014]:5014 pgmaster@testdb=#* select txid_current();
txid_current
--------------
541
(1 row)
Time: 0.596 ms
[local:/opt/data5014]:5014 pgmaster@testdb=#*
-- session 2(GDB跟踪的进程)
[local:/opt/data5014]:5014 pgmaster@testdb=# begin;
BEGIN
Time: 0.299 ms
[local:/opt/data5014]:5014 pgmaster@testdb=#* select * from t1;
-- session 3
[local:/opt/data5014]:5014 pgmaster@testdb=# select txid_current();
txid_current
--------------
542
(1 row)
Time: 4.314 ms
[local:/opt/data5014]:5014 pgmaster@testdb=#
跟踪分析
(gdb) b GetSnapshotData
Breakpoint 1 at 0x924965: file procarray.c, line 2111.
(gdb) c
Continuing.
Breakpoint 1, GetSnapshotData (snapshot=0x103adc0 ) at procarray.c:2111
2111 ProcArrayStruct *arrayP = procArray;
//ProcArrayStruct结构体
(gdb) p *procArray
$3 = {numProcs = 4, maxProcs = 122, maxKnownAssignedXids = 7930, numKnownAssignedXids = 0, tailKnownAssignedXids = 0, headKnownAssignedXids = 0, known_assigned_xids_lck = 0 '\000', lastOverflowedXid = 0,
replication_slot_xmin = 0, replication_slot_catalog_xmin = 0, pgprocnos = 0x7fee7e2148a8}
(gdb) p *procArray->pgprocnos
$4 = 97
(gdb) n
2112 TransactionId *other_xids = ProcGlobal->xids;
//PROC_HDR结构体
(gdb) p *ProcGlobal
$5 = {allProcs = 0x7fee7e1f9300, xids = 0x7fee7e214480, subxidStates = 0x7fee7e214680, statusFlags = 0x7fee7e214780 "", allProcCount = 126, freeProcs = 0x7fee7e20dd00, autovacFreeProcs = 0x7fee7e20f1a0,
bgworkerFreeProcs = 0x7fee7e210d20, walsenderFreeProcs = 0x7fee7e2132f0, procArrayGroupFirst = {value = 2147483647}, clogGroupFirst = {value = 2147483647}, walwriterLatch = 0x7fee7e2139f4,
checkpointerLatch = 0x7fee7e213684, spins_per_delay = 208, startupProc = 0x0, startupProcPid = 0, startupBufferPinWaitBufId = -1}
//PGPROC结构体数组(这里打印了数组的第一个元素)
(gdb) p *ProcGlobal->allProcs
$6 = {links = {prev = 0x0, next = 0x0}, procgloballist = 0x7fee7e1f92a8, sem = 0x7fee75684038, waitStatus = PROC_WAIT_STATUS_OK, procLatch = {is_set = 0, is_shared = true, owner_pid = 0}, xid = 0, xmin = 0, lxid = 0,
pid = 0, pgxactoff = 0, pgprocno = 0, backendId = 0, databaseId = 0, roleId = 0, tempNamespaceId = 0, isBackgroundWorker = false, recoveryConflictPending = false, lwWaiting = false, lwWaitMode = 0 '\000', lwWaitLink = {
next = 0, prev = 0}, cvWaitLink = {next = 0, prev = 0}, waitLock = 0x0, waitProcLock = 0x0, waitLockMode = 0, heldLocks = 0, waitStart = {value = 0}, delayChkpt = false, statusFlags = 0 '\000', waitLSN = 0,
syncRepState = 0, syncRepLinks = {prev = 0x0, next = 0x0}, myProcLocks = {{prev = 0x7fee7e1f93b8, next = 0x7fee7e1f93b8}, {prev = 0x7fee7e1f93c8, next = 0x7fee7e1f93c8}, {prev = 0x7fee7e1f93d8, next = 0x7fee7e1f93d8}, {
prev = 0x7fee7e1f93e8, next = 0x7fee7e1f93e8}, {prev = 0x7fee7e1f93f8, next = 0x7fee7e1f93f8}, {prev = 0x7fee7e1f9408, next = 0x7fee7e1f9408}, {prev = 0x7fee7e1f9418, next = 0x7fee7e1f9418}, {prev = 0x7fee7e1f9428,
next = 0x7fee7e1f9428}, {prev = 0x7fee7e1f9438, next = 0x7fee7e1f9438}, {prev = 0x7fee7e1f9448, next = 0x7fee7e1f9448}, {prev = 0x7fee7e1f9458, next = 0x7fee7e1f9458}, {prev = 0x7fee7e1f9468,
next = 0x7fee7e1f9468}, {prev = 0x7fee7e1f9478, next = 0x7fee7e1f9478}, {prev = 0x7fee7e1f9488, next = 0x7fee7e1f9488}, {prev = 0x7fee7e1f9498, next = 0x7fee7e1f9498}, {prev = 0x7fee7e1f94a8,
next = 0x7fee7e1f94a8}}, subxidStatus = {count = 0 '\000', overflowed = false}, subxids = {xids = {0 }}, procArrayGroupMember = false, procArrayGroupNext = {value = 2147483647},
procArrayGroupMemberXid = 0, wait_event_info = 0, clogGroupMember = false, clogGroupNext = {value = 2147483647}, clogGroupMemberXid = 0, clogGroupMemberXidStatus = 0, clogGroupMemberPage = 0, clogGroupMemberLsn = 0,
fpInfoLock = {tranche = 60, state = {value = 536870912}, waiters = {head = 2147483647, tail = 2147483647}}, fpLockBits = 0, fpRelId = {0 }, fpVXIDLock = false, fpLocalTransactionId = 0,
lockGroupLeader = 0x0, lockGroupMembers = {head = {prev = 0x7fee7e1f9650, next = 0x7fee7e1f9650}}, lockGroupLink = {prev = 0x0, next = 0x0}}
(gdb) p *ProcGlobal->xids
$7 = 0
(gdb) n
2115 size_t count = 0;
(gdb)
2116 int subcount = 0;
(gdb)
2117 bool suboverflowed = false;
(gdb)
//无流复制
2124 TransactionId replication_slot_xmin = InvalidTransactionId;
(gdb)
2125 TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
(gdb)
2127 Assert(snapshot != NULL);
(gdb)
2140 if (snapshot->xip == NULL)
(gdb)
//已共享模式获取ProcArrayLock
2165 LWLockAcquire(ProcArrayLock, LW_SHARED);
(gdb)
//判断快照是否可以重用
2167 if (GetSnapshotDataReuse(snapshot))
(gdb) step
//进入GetSnapshotDataReuse
GetSnapshotDataReuse (snapshot=0x103adc0 ) at procarray.c:2030
2030 Assert(LWLockHeldByMe(ProcArrayLock));
//快照信息
(gdb) p *snapshot
$8 = {snapshot_type = SNAPSHOT_MVCC, xmin = 540, xmax = 540, xip = 0x2c15080, xcnt = 0, subxip = 0x2c6a350, subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = false, curcid = 0,
speculativeToken = 0, vistest = 0x0, active_count = 0, regd_count = 0, ph_node = {first_child = 0x0, next_sibling = 0x0, prev_or_parent = 0x0}, whenTaken = 0, lsn = 0, snapXactCompletionCount = 1}
(gdb) n
2032 if (unlikely(snapshot->snapXactCompletionCount == 0))
(gdb)
2035 curXactCompletionCount = ShmemVariableCache->xactCompletionCount;
(gdb)
//两者不等,不能重用
2036 if (curXactCompletionCount != snapshot->snapXactCompletionCount)
//共享变量缓存
//最后一个已完成事务的xid=542,因此xmax=543
(gdb) p *ShmemVariableCache
$9 = {nextOid = 16388, oidCount = 8189, nextXid = {value = 543}, oldestXid = 532, xidVacLimit = 200000532, xidWarnLimit = 2107484179, xidStopLimit = 2144484179, xidWrapLimit = 2147484179, oldestXidDB = 1,
oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = {value = 542}, xactCompletionCount = 3, oldestClogXid = 532}
(gdb) n
2037 return false;
(gdb)
2073 }
(gdb)
GetSnapshotData (snapshot=0x103adc0 ) at procarray.c:2173
2173 latest_completed = ShmemVariableCache->latestCompletedXid;
(gdb)
2174 mypgxactoff = MyProc->pgxactoff;
(gdb)
2175 myxid = other_xids[mypgxactoff];
//获取快照的进程PGPROC结构体信息
(gdb) p *MyProc
$10 = {links = {prev = 0x0, next = 0x0}, procgloballist = 0x7fee7e1f92a8, sem = 0x7fee756871b8, waitStatus = PROC_WAIT_STATUS_OK, procLatch = {is_set = 0, is_shared = true, owner_pid = 2593}, xid = 0, xmin = 0,
lxid = 10, pid = 2593, pgxactoff = 2, pgprocno = 99, backendId = 3, databaseId = 16384, roleId = 10, tempNamespaceId = 0, isBackgroundWorker = false, recoveryConflictPending = false, lwWaiting = false,
lwWaitMode = 0 '\000', lwWaitLink = {next = 0, prev = 0}, cvWaitLink = {next = 0, prev = 0}, waitLock = 0x0, waitProcLock = 0x0, waitLockMode = 0, heldLocks = 0, waitStart = {value = 0}, delayChkpt = false,
statusFlags = 0 '\000', waitLSN = 0, syncRepState = 0, syncRepLinks = {prev = 0x0, next = 0x0}, myProcLocks = {{prev = 0x7fee7e20e808, next = 0x7fee7e20e808}, {prev = 0x7fee7e20e818, next = 0x7fee7e20e818}, {
prev = 0x7fee7e20e828, next = 0x7fee7e20e828}, {prev = 0x7fee7e20e838, next = 0x7fee7e20e838}, {prev = 0x7fee7e20e848, next = 0x7fee7e20e848}, {prev = 0x7fee7e20e858, next = 0x7fee7e20e858}, {prev = 0x7fee7e20e868,
next = 0x7fee7e20e868}, {prev = 0x7fee7e20e878, next = 0x7fee7e20e878}, {prev = 0x7fee7e20e888, next = 0x7fee7e20e888}, {prev = 0x7fee7e20e898, next = 0x7fee7e20e898}, {prev = 0x7fee7e20e8a8,
next = 0x7fee7e20e8a8}, {prev = 0x7fee7e20e8b8, next = 0x7fee7e20e8b8}, {prev = 0x7fee7e20e8c8, next = 0x7fee7e20e8c8}, {prev = 0x7fee7e20e8d8, next = 0x7fee7e20e8d8}, {prev = 0x7fee7e20e8e8,
next = 0x7fee7e20e8e8}, {prev = 0x7fee7e20e8f8, next = 0x7fee7e20e8f8}}, subxidStatus = {count = 0 '\000', overflowed = false}, subxids = {xids = {0 }}, procArrayGroupMember = false,
procArrayGroupNext = {value = 2147483647}, procArrayGroupMemberXid = 0, wait_event_info = 0, clogGroupMember = false, clogGroupNext = {value = 2147483647}, clogGroupMemberXid = 0, clogGroupMemberXidStatus = 0,
clogGroupMemberPage = -1, clogGroupMemberLsn = 0, fpInfoLock = {tranche = 60, state = {value = 536870912}, waiters = {head = 2147483647, tail = 2147483647}}, fpLockBits = 0, fpRelId = {0 , 2703,
1247}, fpVXIDLock = true, fpLocalTransactionId = 10, lockGroupLeader = 0x0, lockGroupMembers = {head = {prev = 0x7fee7e20eaa0, next = 0x7fee7e20eaa0}}, lockGroupLink = {prev = 0x0, next = 0x0}}
(gdb) p mypgxactoff
$11 = 2
(gdb) p other_xids[mypgxactoff]
$12 = 0
(gdb) n
2176 Assert(myxid == MyProc->xid);
(gdb) p MyProc->xid
$13 = 0
(gdb) n
2178 oldestxid = ShmemVariableCache->oldestXid;
(gdb)
//curXactCompletionCount = 3
2179 curXactCompletionCount = ShmemVariableCache->xactCompletionCount;
(gdb)
2182 xmax = XidFromFullTransactionId(latest_completed);
(gdb)
2183 TransactionIdAdvance(xmax);
(gdb) p xmax
$14 = 542
(gdb) n
2184 Assert(TransactionIdIsNormal(xmax));
//xmax = 543(542 + 1)
(gdb) p xmax
$15 = 543
(gdb) n
//初始化,xmin = xmax
2187 xmin = xmax;
(gdb)
2190 if (TransactionIdIsNormal(myxid) && NormalTransactionIdPrecedes(myxid, xmin))
(gdb) p myxid
$16 = 0
(gdb) n
2193 snapshot->takenDuringRecovery = RecoveryInProgress();
(gdb)
2195 if (!snapshot->takenDuringRecovery)
(gdb)
//不在恢复过程中
2197 size_t numProcs = arrayP->numProcs;
(gdb)
2198 TransactionId *xip = snapshot->xip;
(gdb) p numProcs
$17 = 5
(gdb) n
2199 int *pgprocnos = arrayP->pgprocnos;
(gdb) p *xip
$18 = 2258962920
(gdb) p (int)*xip
$19 = -2036004376
(gdb) n
2200 XidCacheStatus *subxidStates = ProcGlobal->subxidStates;
(gdb)
2201 uint8 *allStatusFlags = ProcGlobal->statusFlags;
(gdb)
//遍历PGPROC数组,这是成本比较高的逻辑
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2210 TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
(gdb)
2213 Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
(gdb) p xid
$20 = 0
(gdb) n
//无效的xid,继续下一循环
2219 if (likely(xid == InvalidTransactionId))
(gdb)
2220 continue;
(gdb)
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2210 TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
(gdb)
2213 Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
(gdb) p xid
$21 = 541
//这是有效的xid
(gdb) n
2219 if (likely(xid == InvalidTransactionId))
(gdb)
//不是当前的事务,是其他事务xid
2227 if (pgxactoff == mypgxactoff)
(gdb)
2236 Assert(TransactionIdIsNormal(xid));
(gdb)
2243 if (!NormalTransactionIdPrecedes(xid, xmax))
(gdb) p xid
$22 = 541
(gdb) p xmax
$23 = 543
(gdb) n
2250 statusFlags = allStatusFlags[pgxactoff];
(gdb)
2251 if (statusFlags & (PROC_IN_LOGICAL_DECODING | PROC_IN_VACUUM))
(gdb) p statusFlags
$24 = 0 '\000'
(gdb) n
2254 if (NormalTransactionIdPrecedes(xid, xmin))
(gdb)
2255 xmin = xid;
(gdb) p xmin
$25 = 543
(gdb) p xid
$26 = 541
(gdb) n
//该事务还没有完成,放在xip数组中
2258 xip[count++] = xid;
(gdb)
2275 if (!suboverflowed)
(gdb) p xip[0]
$27 = 541
(gdb) n
2278 if (subxidStates[pgxactoff].overflowed)
(gdb)
2282 int nsubxids = subxidStates[pgxactoff].count;
(gdb)
2284 if (nsubxids > 0)
(gdb)
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2210 TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
(gdb)
2213 Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
(gdb) p xid
$29 = 0
(gdb) n
2219 if (likely(xid == InvalidTransactionId))
(gdb)
2220 continue;
(gdb)
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2210 TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
(gdb)
2213 Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
(gdb) p xid
$30 = 0
(gdb) n
2219 if (likely(xid == InvalidTransactionId))
(gdb)
2220 continue;
(gdb)
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2210 TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
(gdb)
2213 Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
(gdb) p xid
$31 = 0
(gdb) n
2219 if (likely(xid == InvalidTransactionId))
(gdb)
2220 continue;
//完成PGPROC数组的遍历
(gdb)
2207 for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
(gdb)
2344 replication_slot_xmin = procArray->replication_slot_xmin;
(gdb)
2345 replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
(gdb)
2347 if (!TransactionIdIsValid(MyProc->xmin))
(gdb)
2348 MyProc->xmin = TransactionXmin = xmin;
(gdb)
//释放锁
2350 LWLockRelease(ProcArrayLock);
(gdb)
2365 oldestfxid = FullXidRelativeTo(latest_completed, oldestxid);
(gdb)
2369 TransactionIdRetreatedBy(xmin, vacuum_defer_cleanup_age);
(gdb) p oldestfxid
$32 = {value = 532}
(gdb) p latest_completed
$33 = {value = 542}
(gdb) n
2368 def_vis_xid_data =
(gdb) p xmin
$34 = 541
(gdb) n
2372 def_vis_xid_data =
(gdb)
2379 def_vis_xid = def_vis_xid_data;
(gdb)
2385 def_vis_xid =
(gdb)
2388 def_vis_fxid = FullXidRelativeTo(latest_completed, def_vis_xid);
(gdb)
2389 def_vis_fxid_data = FullXidRelativeTo(latest_completed, def_vis_xid_data);
(gdb) p def_vis_fxid
$35 = {value = 541}
(gdb) n
2396 GlobalVisSharedRels.definitely_needed =
(gdb)
2399 GlobalVisCatalogRels.definitely_needed =
(gdb)
2402 GlobalVisDataRels.definitely_needed =
(gdb)
2406 if (TransactionIdIsNormal(myxid))
//definitely_needed : 大于等于该值的XIDs:其他活动backend可见
//maybe_needed : 小于该值的XIDs:不再视为正在运行,均可见
(gdb) p GlobalVisDataRels
$36 = {definitely_needed = {value = 541}, maybe_needed = {value = 532}}
(gdb) n
2411 GlobalVisTempRels.definitely_needed = latest_completed;
(gdb)
2412 FullTransactionIdAdvance(&GlobalVisTempRels.definitely_needed);
(gdb)
2423 GlobalVisSharedRels.maybe_needed =
(gdb)
2426 GlobalVisCatalogRels.maybe_needed =
(gdb)
2429 GlobalVisDataRels.maybe_needed =
(gdb)
2433 GlobalVisTempRels.maybe_needed = GlobalVisTempRels.definitely_needed;
(gdb)
2436 RecentXmin = xmin;
//临时表
(gdb) p GlobalVisTempRels
$37 = {definitely_needed = {value = 543}, maybe_needed = {value = 543}}
(gdb) n
2437 Assert(TransactionIdPrecedesOrEquals(TransactionXmin, RecentXmin));
(gdb)
2439 snapshot->xmin = xmin;
(gdb)
2440 snapshot->xmax = xmax;
(gdb)
2441 snapshot->xcnt = count;
(gdb)
2442 snapshot->subxcnt = subcount;
(gdb)
2443 snapshot->suboverflowed = suboverflowed;
(gdb)
2444 snapshot->snapXactCompletionCount = curXactCompletionCount;
(gdb)
2446 snapshot->curcid = GetCurrentCommandId(false);
(gdb)
2452 snapshot->active_count = 0;
(gdb)
2453 snapshot->regd_count = 0;
(gdb)
2454 snapshot->copied = false;
(gdb)
2456 GetSnapshotDataInitOldSnapshot(snapshot);
(gdb)
2458 return snapshot;
(gdb)
2459 }
//最后获得的快照信息
(gdb) p *snapshot
$38 = {snapshot_type = SNAPSHOT_MVCC, xmin = 541, xmax = 543, xip = 0x2c15080, xcnt = 1, subxip = 0x2c6a350, subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = false, curcid = 0,
speculativeToken = 0, vistest = 0x0, active_count = 0, regd_count = 0, ph_node = {first_child = 0x0, next_sibling = 0x0, prev_or_parent = 0x0}, whenTaken = 0, lsn = 0, snapXactCompletionCount = 3}
(gdb)