中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PostgreSQL中GetSnapshotData的處理過程是什么

發布時間:2021-11-10 14:19:59 來源:億速云 閱讀:188 作者:iii 欄目:關系型數據庫

這篇文章主要講解了“PostgreSQL中GetSnapshotData的處理過程是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“PostgreSQL中GetSnapshotData的處理過程是什么”吧!

一、數據結構

全局/靜態變量

/*
 * Currently registered Snapshots.  Ordered in a heap by xmin, so that we can
 * quickly find the one with lowest xmin, to advance our MyPgXact->xmin.
 * 當前已注冊的快照.
 * 按照xmin堆排序,這樣我們可以快速找到xmin最小的一個,從而可以設置MyPgXact->xmin。
 */
static int xmin_cmp(const pairingheap_node *a, const pairingheap_node *b,
 void *arg);
static pairingheap RegisteredSnapshots = {&xmin_cmp, NULL, NULL};
/* first GetTransactionSnapshot call in a transaction? */
bool        FirstSnapshotSet = false;
/*
 * Remember the serializable transaction snapshot, if any.  We cannot trust
 * FirstSnapshotSet in combination with IsolationUsesXactSnapshot(), because
 * GUC may be reset before us, changing the value of IsolationUsesXactSnapshot.
 * 如存在則記下serializable事務快照.
 * 我們不能信任與IsolationUsesXactSnapshot()結合使用的FirstSnapshotSet,
 *   因為GUC可能會在我們之前重置,改變IsolationUsesXactSnapshot的值。
 */
static Snapshot FirstXactSnapshot = NULL;
/*
 * CurrentSnapshot points to the only snapshot taken in transaction-snapshot
 * mode, and to the latest one taken in a read-committed transaction.
 * SecondarySnapshot is a snapshot that's always up-to-date as of the current
 * instant, even in transaction-snapshot mode.  It should only be used for
 * special-purpose code (say, RI checking.)  CatalogSnapshot points to an
 * MVCC snapshot intended to be used for catalog scans; we must invalidate it
 * whenever a system catalog change occurs.
 * CurrentSnapshot指向在transaction-snapshot模式下獲取的唯一快照/在read-committed事務中獲取的最新快照。
 * SecondarySnapshot是即使在transaction-snapshot模式下,也總是最新的快照。它應該只用于特殊用途碼(例如,RI檢查)。
 * CatalogSnapshot指向打算用于catalog掃描的MVCC快照;
 *  無論何時發生system catalog更改,我們都必須馬上使其失效。
 *
 * These SnapshotData structs are static to simplify memory allocation
 * (see the hack in GetSnapshotData to avoid repeated malloc/free).
 * 這些SnapshotData結構體是靜態的便于簡化內存分配.
 * (可以回過頭來看GetSnapshotData函數如何避免重復的malloc/free)
 */
static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
/* Pointers to valid snapshots */
//指向有效的快照
static Snapshot CurrentSnapshot = NULL;
static Snapshot SecondarySnapshot = NULL;
static Snapshot CatalogSnapshot = NULL;
static Snapshot HistoricSnapshot = NULL;
/*
 * These are updated by GetSnapshotData.  We initialize them this way
 * for the convenience of TransactionIdIsInProgress: even in bootstrap
 * mode, we don't want it to say that BootstrapTransactionId is in progress.
 * 這些變量通過函數GetSnapshotData更新.
 * 為了便于TransactionIdIsInProgress,以這種方式初始化它們:
 *   即使在引導模式下,我們也不希望表示BootstrapTransactionId正在進行中。
 *
 * RecentGlobalXmin and RecentGlobalDataXmin are initialized to
 * InvalidTransactionId, to ensure that no one tries to use a stale
 * value. Readers should ensure that it has been set to something else
 * before using it.
 * RecentGlobalXmin和RecentGlobalDataXmin初始化為InvalidTransactionId,
 *   以確保沒有人嘗試使用過時的值。
 * 在使用它之前,讀取進程應確保它已經被設置為其他值。
 */
TransactionId TransactionXmin = FirstNormalTransactionId;
TransactionId RecentXmin = FirstNormalTransactionId;
TransactionId RecentGlobalXmin = InvalidTransactionId;
TransactionId RecentGlobalDataXmin = InvalidTransactionId;
/* (table, ctid) => (cmin, cmax) mapping during timetravel */
static HTAB *tuplecid_data = NULL;

MyPgXact
當前的事務信息.

/*
 * Flags for PGXACT->vacuumFlags
 * PGXACT->vacuumFlags標記
 *
 * Note: If you modify these flags, you need to modify PROCARRAY_XXX flags
 * in src/include/storage/procarray.h.
 * 注意:如果修改了這些標記,需要更新src/include/storage/procarray.h中的PROCARRAY_XXX標記
 *
 * PROC_RESERVED may later be assigned for use in vacuumFlags, but its value is
 * used for PROCARRAY_SLOTS_XMIN in procarray.h, so GetOldestXmin won't be able
 * to match and ignore processes with this flag set.
 * PROC_RESERVED可能在接下來分配給vacuumFlags使用,
 *   但是它在procarray.h中用于標識PROCARRAY_SLOTS_XMIN,
 *   因此GetOldestXmin不能匹配和忽略使用此標記的進程.
 */
//是否auto vacuum worker?
#define     PROC_IS_AUTOVACUUM  0x01    /* is it an autovac worker? */
//正在運行lazy vacuum
#define     PROC_IN_VACUUM      0x02    /* currently running lazy vacuum */
//正在運行analyze
#define     PROC_IN_ANALYZE     0x04    /* currently running analyze */
//只能通過auto vacuum設置
#define     PROC_VACUUM_FOR_WRAPAROUND  0x08    /* set by autovac only */
//在事務外部正在執行邏輯解碼
#define     PROC_IN_LOGICAL_DECODING    0x10    /* currently doing logical
                                                 * decoding outside xact */
//保留用于procarray
#define     PROC_RESERVED               0x20    /* reserved for procarray */
/* flags reset at EOXact */
//在EOXact時用于重置標記的MASK
#define     PROC_VACUUM_STATE_MASK \
    (PROC_IN_VACUUM | PROC_IN_ANALYZE | PROC_VACUUM_FOR_WRAPAROUND)
/*
 * Prior to PostgreSQL 9.2, the fields below were stored as part of the
 * PGPROC.  However, benchmarking revealed that packing these particular
 * members into a separate array as tightly as possible sped up GetSnapshotData
 * considerably on systems with many CPU cores, by reducing the number of
 * cache lines needing to be fetched.  Thus, think very carefully before adding
 * anything else here.
 */
typedef struct PGXACT
{
    //當前的頂層事務ID(非子事務)
    //出于優化的目的,只讀事務并不會分配事務號(xid = 0)
    TransactionId xid;          /* id of top-level transaction currently being
                                 * executed by this proc, if running and XID
                                 * is assigned; else InvalidTransactionId */
    //在啟動事務時,當前正在執行的最小事務號XID,但不包括LAZY VACUUM
    //vacuum不能清除刪除事務號xid >= xmin的元組
    TransactionId xmin;         /* minimal running XID as it was when we were
                                 * starting our xact, excluding LAZY VACUUM:
                                 * vacuum must not remove tuples deleted by
                                 * xid >= xmin ! */
    //vacuum相關的標記
    uint8       vacuumFlags;    /* vacuum-related flags, see above */
    bool        overflowed;
    bool        delayChkpt;     /* true if this proc delays checkpoint start;
                                 * previously called InCommit */
    uint8       nxids;
} PGXACT;
extern PGDLLIMPORT struct PGXACT *MyPgXact;

Snapshot
SnapshotData結構體指針,SnapshotData結構體可表達的信息囊括了所有可能的快照.
有以下幾種不同類型的快照:
1.常規的MVCC快照
2.在恢復期間的MVCC快照(處于Hot-Standby模式)
3.在邏輯解碼過程中使用的歷史MVCC快照
4.作為參數傳遞給HeapTupleSatisfiesDirty()函數的快照
5.作為參數傳遞給HeapTupleSatisfiesNonVacuumable()函數的快照
6.用于在沒有成員訪問情況下SatisfiesAny、Toast和Self的快照

//SnapshotData結構體指針
typedef struct SnapshotData *Snapshot;
//無效的快照
#define InvalidSnapshot     ((Snapshot) NULL)
/*
 * 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 the "satisfies"
 * function.
 * 我們使用SnapshotData結構體表示"regular" (MVCC) snapshots和具有非MVCC語義的"special" snapshots。
 */
//測試函數
typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
                                       Snapshot snapshot, Buffer buffer);
//常見的有:
//HeapTupleSatisfiesMVCC:判斷元組對某一快照版本是否有效
//HeapTupleSatisfiesUpdate:判斷元組是否可更新(同時更新同一個元組)
//HeapTupleSatisfiesDirty:判斷當前元組是否存在臟數據
//HeapTupleSatisfiesSelf:判斷tuple對自身信息是否有效
//HeapTupleSatisfiesToast:判斷是否TOAST表
//HeapTupleSatisfiesVacuum:判斷元組是否能被VACUUM刪除
//HeapTupleSatisfiesAny:所有元組都可見
//HeapTupleSatisfiesHistoricMVCC:用于CATALOG 表
/*
 * 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.
 * 有以下幾種不同類型的快照:
 * * 常規的MVCC快照
 * * 在恢復期間的MVCC快照(處于Hot-Standby模式)
 * * 在邏輯解碼過程中使用的歷史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: 使用類似于parser/executor nodes的處理,使用NodeTag來拆分結構體會是一個好的做法,
 *       使用OO(面向對象繼承)的方法.
 */
typedef struct SnapshotData
{
    //測試tuple是否可見的函數
    SnapshotSatisfiesFunc satisfies;    /* tuple test function */
    /*
     * 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.
     *  XIDs >= xmax的事務,對該快照是不可見的(沒有任何影響).
     * 對該快照可見的是小于xmax,但不在snapshot列表中的XIDs.
     * 記錄xmin是出于優化的目的,避免為大多數tuples搜索XID數組.
     */
    //XID ∈ [2,min)是可見的 
    TransactionId xmin;         /* all XID < xmin are visible to me */
    //XID ∈ [xmax,∞)是不可見的
    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.
     * 對于普通的MVCC快照,xip存儲了所有正在進行中的XIDs,除非在恢復期間產生的快照(這時候數組為空)
     * 對于歷史MVCC快照,意義相反,即它包含xmin和xmax之間的*已提交*事務。
     *
     * note: all ids in xip[] satisfy xmin <= xip[i] < xmax
     * 注意: 所有在xip數組中的XIDs滿足xmin <= xip[i] < xmax
     */
    TransactionId *xip;
    //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快照,下面這些域含有活動的subxact IDs.
     *   (以及在恢復過程中狀態為進行中的事務).
     * 對于歷史MVCC快照,這些域字段含有*所有*用于回放事務的快照,包括頂層事務XIDs.
     *
     * note: all ids in subxip[] are >= xmin, but we don't bother filtering
     * out any that are >= xmax
     * 注意:sbuxip數組中的元素均≥ xmin,但我們不需要過濾掉任何>= xmax的項
     */
    TransactionId *subxip;
    //subxip數組元素個數
    int32       subxcnt;        /* # of xact ids in subxip[] */
    //是否溢出?
    bool        suboverflowed;  /* has the subxip array overflowed? */
    //在Recovery期間的快照?
    bool        takenDuringRecovery;    /* recovery-shaped snapshot? */
    //如為靜態快照,則該值為F
    bool        copied;         /* false if it's a static snapshot */
    //在自身的事務中,CID < curcid是可見的
    CommandId   curcid;         /* in my xact, CID < curcid are visible */
    /*
     * An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
     * snapshots.
     * HeapTupleSatisfiesDirty返回的值,在MVCC快照中無用
     */
    uint32      speculativeToken;
    /*
     * Book-keeping information, used by the snapshot manager
     * 用于快照管理器的Book-keeping信息
     */
    //在ActiveSnapshot棧中的引用計數
    uint32      active_count;   /* refcount on ActiveSnapshot stack */
    //在RegisteredSnapshots中的引用計數
    uint32      regd_count;     /* refcount on RegisteredSnapshots */
    //RegisteredSnapshots堆中的鏈接
    pairingheap_node ph_node;   /* link in the RegisteredSnapshots heap */
    //快照"拍攝"時間戳
    TimestampTz whenTaken;      /* timestamp when snapshot was taken */
    //拍照時WAL stream中的位置
    XLogRecPtr  lsn;            /* position in the WAL stream when taken */
} SnapshotData;

ShmemVariableCache
VariableCache是共享內存中的一種數據結構,用于跟蹤OID和XID分配狀態。
ShmemVariableCache是VariableCache結構體指針.

/*
 * 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;

二、源碼解讀

GetSnapshotData函數返回快照信息.
重點是構造xmin : xmax : xip_list,其實現邏輯簡單總結如下:
1.獲取xmax = ShmemVariableCache->latestCompletedXid + 1;
2.遍歷全局procArray數組,構建快照信息
 2.1 獲取進程相應的事務信息pgxact
 2.2 獲取進程事務ID(pgxact->xid),取最小的xid作為xmin(不包括0)
 2.3 把xid放入快照->xip數組中(不包括本進程所在的事務id)

/*
 * GetSnapshotData -- returns information about running transactions.
 * GetSnapshotData -- 返回關于正在運行中的事務的相關信息
 *
 * The returned snapshot includes xmin (lowest still-running xact ID),
 * xmax (highest completed xact ID + 1), and a list of running xact IDs
 * in the range xmin <= xid < xmax.  It is used as follows:
 *      All xact IDs < xmin are considered finished.
 *      All xact IDs >= xmax are considered still running.
 *      For an xact ID xmin <= xid < xmax, consult list to see whether
 *      it is considered running or not.
 * This ensures that the set of transactions seen as "running" by the
 * current xact will not change after it takes the snapshot.
 * 返回的snapshot包括xmin(最小的正在運行的事務ID),xmax(已完結事務ID + 1),
 *   以及在xmin <= xid < xmax之間正在運行的事務IDs.
 * 意義如下:
 *   事務IDs < xmin是已確定完成的事務.
 *   事務IDs >= xmax是正在運行的事務.
 *   對于XID ∈ [xmin,xmax)的事務,需查閱列表確認是否正在運行中
 *
 * All running top-level XIDs are included in the snapshot, except for lazy
 * VACUUM processes.  We also try to include running subtransaction XIDs,
 * but since PGPROC has only a limited cache area for subxact XIDs, full
 * information may not be available.  If we find any overflowed subxid arrays,
 * we have to mark the snapshot's subxid data as overflowed, and extra work
 * *may* need to be done to determine what's running (see XidInMVCCSnapshot()
 * in tqual.c).
 * 所有正在運行的頂層XIDs包含在快照中,除了lazy VACUUM進程.
 * 我們嘗試包含所有正在運行的子事務XIDs,但由于PGPROC只有有限的緩存,包含所有的子事務信息暫未實現.
 * 如果我們搜索溢出的子事務數組,我們必須標記快照的subxid數據為溢出,
 * 而且需要執行額外的工作以確定哪些在運行(查看tqual.c中的XidInMVCCSnapshot()函數)
 *
 * We also update the following backend-global variables:
 *      TransactionXmin: the oldest xmin of any snapshot in use in the
 *          current transaction (this is the same as MyPgXact->xmin).
 *      RecentXmin: the xmin computed for the most recent snapshot.  XIDs
 *          older than this are known not running any more.
 *      RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
 *          running transactions, except those running LAZY VACUUM).  This is
 *          the same computation done by
 *          GetOldestXmin(NULL, PROCARRAY_FLAGS_VACUUM).
 *      RecentGlobalDataXmin: the global xmin for non-catalog tables
 *          >= RecentGlobalXmin
 * 我們同時更新了以下后臺全局變量:
 *      TransactionXmin: 當前事務中在所有仍在使用的快照中最舊的xmin(與MyPgXact->xmin一致).
 *      RecentXmin: 最近快照的xmin.小于xmin的事務已知已完結.
 *      RecentGlobalXmin:全局的xmin(除了正在運行的LAZY VACUUM,跨越所有正在運行事務的最舊的TransactionXmin),
 *                       這是使用同樣的規則,通過GetOldestXmin(NULL, PROCARRAY_FLAGS_VACUUM)處理.
 *      RecentGlobalDataXmin:非catalog數據表的全局xmin,該值>= RecentGlobalXmin.
 *
 * Note: this function should probably not be called with an argument that's
 * not statically allocated (see xip allocation below).
 * 注意:不應該使用非靜態分配的參數調用這個函數(參見下面的xip分配)。
 */
Snapshot
GetSnapshotData(Snapshot snapshot)
{
    ProcArrayStruct *arrayP = procArray;//進程數組
    TransactionId xmin;//xmin
    TransactionId xmax;//xmax
    TransactionId globalxmin;//全局xmin
    int         index;
    int         count = 0;
    int         subcount = 0;
    bool        suboverflowed = false;
    TransactionId replication_slot_xmin = InvalidTransactionId;
    TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
    Assert(snapshot != NULL);
    /*
     * Allocating space for maxProcs xids is usually overkill; numProcs would
     * be sufficient.  But it seems better to do the malloc while not holding
     * the lock, so we can't look at numProcs.  Likewise, we allocate much
     * more subxip storage than is probably needed.
     * 為maxProcs xids分配空間通常是多余的;numProcs就足夠了。
     * 但是在不持有鎖的情況下執行malloc似乎更好,因此我們不能查看numProcs。
     * 同樣地,我們分配的子xip存儲可能比實際需要的多得多。
     *
     * This does open a possibility for avoiding repeated malloc/free: since
     * maxProcs does not change at runtime, we can simply reuse the previous
     * xip arrays if any.  (This relies on the fact that all callers pass
     * static SnapshotData structs.)
     * 這確實為避免重復的malloc/free創造了一種可能性:因為maxProcs在運行時不會改變,
     *   如果有的話,我們可以簡單地重用前面的xip數組。
     * (這依賴于所有調用者都傳遞靜態快照數據結構這一事實。)
     */
    if (snapshot->xip == NULL)
    {
        /*
         * First call for this snapshot. Snapshot is same size whether or not
         * we are in recovery, see later comments.
         * 首次調用.快照的大小不管是在常規還是在恢復狀態都是一樣的,看稍后的注釋.
         */
        snapshot->xip = (TransactionId *)
            malloc(GetMaxSnapshotXidCount() * sizeof(TransactionId));
        if (snapshot->xip == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
        Assert(snapshot->subxip == NULL);
        snapshot->subxip = (TransactionId *)
            malloc(GetMaxSnapshotSubxidCount() * sizeof(TransactionId));
        if (snapshot->subxip == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
    }
    /*
     * It is sufficient to get shared lock on ProcArrayLock, even if we are
     * going to set MyPgXact->xmin.
     * 即使我們要設置MyPgXact->xmin,也需要獲取鎖,在ProcArrayLock上獲得共享鎖就足夠了.
     * 
     */
    LWLockAcquire(ProcArrayLock, LW_SHARED);
    /* xmax is always latestCompletedXid + 1 */
    //xmax = latestCompletedXid + 1
    //已完結事務號 + 1
    xmax = ShmemVariableCache->latestCompletedXid;
    Assert(TransactionIdIsNormal(xmax));
    TransactionIdAdvance(xmax);// + 1
    /* initialize xmin calculation with xmax */
    //初始化xmin為xmax
    globalxmin = xmin = xmax;
    //是否處于恢復過程中?
    snapshot->takenDuringRecovery = RecoveryInProgress();
    if (!snapshot->takenDuringRecovery)
    {
        //不是,正常運行中
        int        *pgprocnos = arrayP->pgprocnos;//進程數
        int         numProcs;
        /*
         * Spin over procArray checking xid, xmin, and subxids.  The goal is
         * to gather all active xids, find the lowest xmin, and try to record
         * subxids.
         * Spin Over procArray,檢查xid/xmin和subxids.
         * 目標是搜集所有活動的xids,找到最小的xmin,并嘗試記錄subxids.
         */
        numProcs = arrayP->numProcs;
        for (index = 0; index < numProcs; index++)//遍歷procArray數組
        {
            int         pgprocno = pgprocnos[index];//allPgXact[]索引
            PGXACT     *pgxact = &allPgXact[pgprocno];//獲取PGXACT
            TransactionId xid;//事務id
            /*
             * Skip over backends doing logical decoding which manages xmin
             * separately (check below) and ones running LAZY VACUUM.
             * 跳過正在執行邏輯解碼(單獨管理xmin)和執行LAZY VACUUM的進程.
             * 
             */
            if (pgxact->vacuumFlags &
                (PROC_IN_LOGICAL_DECODING | PROC_IN_VACUUM))
                continue;
            /* Update globalxmin to be the smallest valid xmin */
            //更新globalxmin為最小有效的xmin
            xid = UINT32_ACCESS_ONCE(pgxact->xmin);//獲取進程事務的xmin
            if (TransactionIdIsNormal(xid) &&
                NormalTransactionIdPrecedes(xid, globalxmin))
                globalxmin = xid;
            /* Fetch xid just once - see GetNewTransactionId */
            //只提取一次xid -- 查看函數GetNewTransactionId
            xid = UINT32_ACCESS_ONCE(pgxact->xid);
            /*
             * If the transaction has no XID assigned, we can skip it; it
             * won't have sub-XIDs either.  If the XID is >= xmax, we can also
             * skip it; such transactions will be treated as running anyway
             * (and any sub-XIDs will also be >= xmax).
             * 如果事務未分配XID事務號,跳過此事務.該事務也不會含有子事務.
             * 如果XID >= xmax,我們也可以跳過,這些事務可被處理為正在運行的思維.
             *   (這些事務的子事務XID也同樣會 >= xmax)
             */
            if (!TransactionIdIsNormal(xid)
                || !NormalTransactionIdPrecedes(xid, xmax))
                continue;
            /*
             * We don't include our own XIDs (if any) in the snapshot, but we
             * must include them in xmin.
             * 在快照中,不會包含自己的XIDs,但必須體現在xmin中
             */
            if (NormalTransactionIdPrecedes(xid, xmin))
                //xid 小于 xmin,設置為xid
                xmin = xid;
            if (pgxact == MyPgXact)
                continue;//跳過本事務
            /* Add XID to snapshot. */
            //添加XID到快照中
            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.
             * 如可能,保存子事務XIDs(如果已經溢出,那就沒法了).
             * 注意子事務XIDs必須在他們的父事務之后發生,因此無需檢查xmin.
             * 我們可以利用xmax進行過濾,但是在持有鎖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.)
             * 其他后臺進程可能并發增加子事務ID,但不能清除.
             * 因此,只取一次nxids很重要.不過,使用memcpy是安全的.
             * (不需要擔心遺漏并發增加xids,因為他們在xmax之后)
             *
             * Again, our own XIDs are not included in the snapshot.
             * 再次,我們自己的XIDs不需要包含在快照中
             */
            if (!suboverflowed)
            {
                if (pgxact->overflowed)
                    suboverflowed = true;
                else
                {
                    int         nxids = pgxact->nxids;
                    if (nxids > 0)
                    {
                        PGPROC     *proc = &allProcs[pgprocno];
                        pg_read_barrier();  /* pairs with GetNewTransactionId */
                        memcpy(snapshot->subxip + subcount,
                               (void *) proc->subxids.xids,
                               nxids * sizeof(TransactionId));
                        subcount += nxids;
                    }
                }
            }
        }
    }
    else
    {
        /*
         * We're in hot standby, so get XIDs from KnownAssignedXids.
         * 處于hot standby中,通過KnownAssignedXids獲取XIDs.
         *
         * We store all xids directly into subxip[]. Here's why:
         * 直接存儲所有的xids到subxip[]中,這是因為:
         *
         * In recovery we don't know which xids are top-level and which are
         * subxacts, a design choice that greatly simplifies xid processing.
         * 在恢復過程中,我們不需要知道哪些xids是頂層事務,哪些是子事務,
         * 這可以極大的簡化xid處理過程.
         * 
         * 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.
         * 似乎我們只想把xid放到xip[]中,但xip數組是相當小的。
         *   我們要么需要擴展,要么提高WAL-log xid分派的速度;
         *   但這兩個選擇都不吸引人。
         *
         * 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.
         * 如果xid太多的話,我們嘗試先將xid存儲到xip[]中,然后再在subxip[]中存儲。
         * 這只在快照沒有溢出的情況下有效,因為在這種情況下我們不搜索subxip[]。
         * 一種更簡單的方法是將所有xid存儲在subxact數組中,因為這個數組要大得多。
         * 讓xip數組為空。
         *
         * 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.
         * 無論哪種方式,我們都需要根據快照的拍攝時間更改XidInMVCCSnapshot()的工作方式,
         *   或者更改正常的快照處理,使其匹配。
         *
         * 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.
         * 注意:在我們完成快照之前,恢復可能會結束,
         *   并且新分配的事務id可能會添加到ProcArray中。
         * 當我們持有鎖ProcArrayLock時,xmax無法更改,
         *   因此那些新添加的事務id將被過濾掉,因此無需擔心。
         */
        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.
     * 持有ProcArrayLock鎖時,提前到本地變量中,
     *   下面的LWLockRelease是一個屏障,確保這發生在鎖內部。
     */
    replication_slot_xmin = procArray->replication_slot_xmin;
    replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
    if (!TransactionIdIsValid(MyPgXact->xmin))
        MyPgXact->xmin = TransactionXmin = xmin;
    LWLockRelease(ProcArrayLock);
    /*
     * Update globalxmin to include actual process xids.  This is a slightly
     * different way of computing it than GetOldestXmin uses, but should give
     * the same result.
     * 更新globalxmin已包含實際的進程xids.
     * 這是一種與GetOldestXmin使用的計算方法略有不同的方法,但是應該會得到相同的結果。
     */
    if (TransactionIdPrecedes(xmin, globalxmin))
        globalxmin = xmin;
    /* Update global variables too */
    //更新全局變量
    RecentGlobalXmin = globalxmin - vacuum_defer_cleanup_age;
    if (!TransactionIdIsNormal(RecentGlobalXmin))
        RecentGlobalXmin = FirstNormalTransactionId;
    /* Check whether there's a replication slot requiring an older xmin. */
    //檢查是否存在正在請求更舊xmin的復制slot
    if (TransactionIdIsValid(replication_slot_xmin) &&
        NormalTransactionIdPrecedes(replication_slot_xmin, RecentGlobalXmin))
        RecentGlobalXmin = replication_slot_xmin;
    /* Non-catalog tables can be vacuumed if older than this xid */
    //比該xid小的非catalog表可被vacuum進程清除
    RecentGlobalDataXmin = RecentGlobalXmin;
    /*
     * Check whether there's a replication slot requiring an older catalog
     * xmin.
     * 檢查是否存在正確請求更舊catalog xmin的復制slot
     */
    if (TransactionIdIsNormal(replication_slot_catalog_xmin) &&
        NormalTransactionIdPrecedes(replication_slot_catalog_xmin, RecentGlobalXmin))
        RecentGlobalXmin = replication_slot_catalog_xmin;
    RecentXmin = xmin;
    snapshot->xmin = xmin;
    snapshot->xmax = xmax;
    snapshot->xcnt = count;
    snapshot->subxcnt = subcount;
    snapshot->suboverflowed = suboverflowed;
    //當前命令id
    snapshot->curcid = GetCurrentCommandId(false);
    /*
     * This is a new snapshot, so set both refcounts are zero, and mark it as
     * not copied in persistent memory.
     * 這是一個新的快照,因此設置refcounts為0,并標記其未在持久化內存中拷貝.
     */
    snapshot->active_count = 0;
    snapshot->regd_count = 0;
    snapshot->copied = false;
    if (old_snapshot_threshold < 0)
    {
        /*
         * If not using "snapshot too old" feature, fill related fields with
         * dummy values that don't require any locking.
         * 如啟用"snapshot too old"特性,使用虛擬值填充相關的字段,這里不需要鎖.
         */
        snapshot->lsn = InvalidXLogRecPtr;
        snapshot->whenTaken = 0;
    }
    else
    {
        /*
         * Capture the current time and WAL stream location in case this
         * snapshot becomes old enough to need to fall back on the special
         * "old snapshot" logic.
         * 捕獲當前時間和WAL流位置,以防快照變得足夠舊時需要使用特殊的“old snapshot”邏輯。
         */
        snapshot->lsn = GetXLogInsertRecPtr();
        snapshot->whenTaken = GetSnapshotCurrentTimestamp();
        MaintainOldSnapshotTimeMapping(snapshot->whenTaken, xmin);
    }
    //返回快照
    return snapshot;
}

三、跟蹤分析

執行簡單查詢,可觸發獲取快照邏輯.

16:35:08 (xdb@[local]:5432)testdb=# begin;
BEGIN
16:35:13 (xdb@[local]:5432)testdb=#* select 1;

啟動gdb,設置斷點

(gdb) b GetSnapshotData
Breakpoint 1 at 0x89aef3: file procarray.c, line 1519.
(gdb) c
Continuing.
Breakpoint 1, GetSnapshotData (snapshot=0xf9be60 <CurrentSnapshotData>) at procarray.c:1519
1519        ProcArrayStruct *arrayP = procArray;
(gdb)

輸入參數snapshot,實質是全局變量CurrentSnapshotData

(gdb) p *snapshot
$1 = {satisfies = 0xa9310d <HeapTupleSatisfiesMVCC>, xmin = 2354, xmax = 2358, xip = 0x24c7e40, xcnt = 1, 
  subxip = 0x251dfa0, subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = false, curcid = 0, 
  speculativeToken = 0, active_count = 0, regd_count = 0, ph_node = {first_child = 0x0, next_sibling = 0x0, 
    prev_or_parent = 0x0}, whenTaken = 0, lsn = 0}

查看共享內存(ShmemVariableCache)中的信息.
nextXID = 2358,下一個待分配的事務ID = 2358.

(gdb) p *ShmemVariableCache
$2 = {nextOid = 42605, oidCount = 8183, nextXid = 2358, oldestXid = 561, xidVacLimit = 200000561, 
  xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, 
  oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2357, oldestClogXid = 561}
(gdb)

獲取全局進程數組procArray,賦值->arrayP.
初始化相關變量.

(gdb) n
1524        int         count = 0;
(gdb) n
1525        int         subcount = 0;
(gdb) 
1526        bool        suboverflowed = false;
(gdb) 
1527        volatile TransactionId replication_slot_xmin = InvalidTransactionId;
(gdb) 
1528        volatile TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
(gdb) 
1530        Assert(snapshot != NULL);
(gdb) 
1543        if (snapshot->xip == NULL)
(gdb)

查看進程數組信息和allPgXact[]數組編號(arrayP->pgprocnos數組).
allPgXact定義:static PGXACT *allPgXact;

(gdb) p *arrayP
$3 = {numProcs = 5, maxProcs = 112, maxKnownAssignedXids = 7280, numKnownAssignedXids = 0, tailKnownAssignedXids = 0, 
  headKnownAssignedXids = 0, known_assigned_xids_lck = 0 '\000', lastOverflowedXid = 0, replication_slot_xmin = 0, 
  replication_slot_catalog_xmin = 0, pgprocnos = 0x7f8765d9a3a8}
(gdb) p arrayP->pgprocnos[0]
$4 = 97
(gdb) p arrayP->pgprocnos[1]
$5 = 98
(gdb) p arrayP->pgprocnos[2]
$6 = 99
(gdb) p arrayP->pgprocnos[3]
$7 = 103
(gdb) p arrayP->pgprocnos[4]
$9 = 111

加鎖,獲取/修改相關信息

(gdb) 
1568        LWLockAcquire(ProcArrayLock, LW_SHARED);

計算xmax

(gdb) n
1571        xmax = ShmemVariableCache->latestCompletedXid;
(gdb) 
1572        Assert(TransactionIdIsNormal(xmax));
(gdb) p xmax
$10 = 2357
(gdb) n
1573        TransactionIdAdvance(xmax);
(gdb) 
1576        globalxmin = xmin = xmax;
(gdb) 
1578        snapshot->takenDuringRecovery = RecoveryInProgress();
(gdb) p xmax
$11 = 2358

判斷是否處于恢復狀態,當前不是恢復狀態,進入相應的處理邏輯

(gdb) n
1580        if (!snapshot->takenDuringRecovery)
(gdb) p snapshot->takenDuringRecovery
$13 = false
(gdb) n
1582            int        *pgprocnos = arrayP->pgprocnos;
(gdb)

獲取進程數和PGXACT索引數組,準備遍歷

(gdb) n
1590            numProcs = arrayP->numProcs;
(gdb) 
1591            for (index = 0; index < numProcs; index++)
(gdb) 
(gdb) p *pgprocnos
$14 = 97
(gdb) p numProcs
$15 = 5
(gdb)

獲取pgxact信息

(gdb) n
1593                int         pgprocno = pgprocnos[index];
(gdb) 
1594                volatile PGXACT *pgxact = &allPgXact[pgprocno];
(gdb) 
1601                if (pgxact->vacuumFlags & PROC_IN_LOGICAL_DECODING)
(gdb) 
1605                if (pgxact->vacuumFlags & PROC_IN_VACUUM)
(gdb) 
1609                xid = pgxact->xmin; /* fetch just once */
(gdb) p *pgxact
$16 = {xid = 0, xmin = 0, vacuumFlags = 0 '\000', overflowed = false, delayChkpt = false, nxids = 0 '\000'}
(gdb)

不是正常的xid,下一個pgxact

(gdb) n
1610                if (TransactionIdIsNormal(xid) &&
(gdb) 
1615                xid = pgxact->xid;
(gdb) 
1623                if (!TransactionIdIsNormal(xid)
(gdb) p xid
$17 = 0
(gdb) n
1625                    continue;
(gdb)

下一個xid = 2355,正常的事務ID

(gdb) 
1591            for (index = 0; index < numProcs; index++)
(gdb) 
1593                int         pgprocno = pgprocnos[index];
(gdb) 
1594                volatile PGXACT *pgxact = &allPgXact[pgprocno];
(gdb) 
1601                if (pgxact->vacuumFlags & PROC_IN_LOGICAL_DECODING)
(gdb) p *pgxact
$18 = {xid = 2355, xmin = 0, vacuumFlags = 0 '\000', overflowed = false, delayChkpt = false, nxids = 0 '\000'}
(gdb)

進行處理

(gdb) n
1605                if (pgxact->vacuumFlags & PROC_IN_VACUUM)
(gdb) 
1609                xid = pgxact->xmin; /* fetch just once */
(gdb) 
1610                if (TransactionIdIsNormal(xid) &&
(gdb) 
1615                xid = pgxact->xid;
(gdb) 
1623                if (!TransactionIdIsNormal(xid)
(gdb) 
1624                    || !NormalTransactionIdPrecedes(xid, xmax))
(gdb) 
1631                if (NormalTransactionIdPrecedes(xid, xmin))
(gdb) p xid
$19 = 2355
(gdb) p xmin
$20 = 2358
(gdb) n
1632                    xmin = xid;
(gdb) 
1633                if (pgxact == MyPgXact)
(gdb)

這是同一個xact,處理下一個xact

(gdb) 
1633                if (pgxact == MyPgXact)
(gdb) p pgxact
$21 = (volatile PGXACT *) 0x7f8765d9a218
(gdb) p MyPgXact
$22 = (struct PGXACT *) 0x7f8765d9a218
(gdb) n
1634                    continue;
(gdb)

下一個是2354

...
(gdb) p *pgxact
$23 = {xid = 2354, xmin = 0, vacuumFlags = 0 '\000', overflowed = false, delayChkpt = false, nxids = 0 '\000'}
(gdb)

xmin調整為2354

1631                if (NormalTransactionIdPrecedes(xid, xmin))
(gdb) 
1632                    xmin = xid;
(gdb) 
1633                if (pgxact == MyPgXact)
(gdb) p xmin
$24 = 2354
(gdb)

寫入到xip_list中

1637                snapshot->xip[count++] = xid;
(gdb) 
1654                if (!suboverflowed)
(gdb) 
(gdb) p count
$25 = 1

繼續循環,完成5個pgxact的遍歷

1591            for (index = 0; index < numProcs; index++)
(gdb) 
1715        replication_slot_xmin = procArray->replication_slot_xmin;
(gdb)

無復制信息

(gdb) 
1715        replication_slot_xmin = procArray->replication_slot_xmin;
(gdb) p procArray->replication_slot_xmin
$28 = 0
(gdb) n
1716        replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
(gdb) 
1718        if (!TransactionIdIsValid(MyPgXact->xmin))

調整本進程的事務信息

(gdb) n
1719            MyPgXact->xmin = TransactionXmin = xmin;
(gdb) p MyPgXact->xmin
$29 = 0
(gdb) n

釋放鎖

1721        LWLockRelease(ProcArrayLock);
(gdb) 
1728        if (TransactionIdPrecedes(xmin, globalxmin))
(gdb)

調整全局xmin

(gdb) p xmin
$30 = 2354
(gdb) p globalxmin
$31 = 2358
(gdb) n
1729            globalxmin = xmin;
(gdb)

更新其他信息

(gdb) 
1732        RecentGlobalXmin = globalxmin - vacuum_defer_cleanup_age;
(gdb) p RecentGlobalXmin
$32 = 2354
(gdb) p vacuum_defer_cleanup_age
$33 = 0
(gdb) n
1733        if (!TransactionIdIsNormal(RecentGlobalXmin))
(gdb) 
1737        if (TransactionIdIsValid(replication_slot_xmin) &&
(gdb) 
1742        RecentGlobalDataXmin = RecentGlobalXmin;
(gdb) p RecentGlobalXmin
$34 = 2354
(gdb) n
1748        if (TransactionIdIsNormal(replication_slot_catalog_xmin) &&
(gdb)

填充snapshot域字段信息

(gdb) 
1752        RecentXmin = xmin;
(gdb) 
1754        snapshot->xmin = xmin;
(gdb) 
1755        snapshot->xmax = xmax;
(gdb) 
1756        snapshot->xcnt = count;
(gdb) 
1757        snapshot->subxcnt = subcount;
(gdb) 
1758        snapshot->suboverflowed = suboverflowed;
(gdb) 
1760        snapshot->curcid = GetCurrentCommandId(false);
(gdb) 
1766        snapshot->active_count = 0;
(gdb) 
1767        snapshot->regd_count = 0;
(gdb) 
1768        snapshot->copied = false;
(gdb) 
1770        if (old_snapshot_threshold < 0)
(gdb) 
1776            snapshot->lsn = InvalidXLogRecPtr;
(gdb) 
1777            snapshot->whenTaken = 0;
(gdb) 
1791        return snapshot;
(gdb)

返回snapshot

(gdb) p snapshot
$35 = (Snapshot) 0xf9be60 <CurrentSnapshotData>
(gdb) p *snapshot
$36 = {satisfies = 0xa9310d <HeapTupleSatisfiesMVCC>, xmin = 2354, xmax = 2358, xip = 0x24c7e40, xcnt = 1, 
  subxip = 0x251dfa0, subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = false, curcid = 0, 
  speculativeToken = 0, active_count = 0, regd_count = 0, ph_node = {first_child = 0x0, next_sibling = 0x0, 
    prev_or_parent = 0x0}, whenTaken = 0, lsn = 0}
(gdb)

注意:snapshot->satisfies函數在初始化該全局變量已設置為HeapTupleSatisfiesMVCC.

感謝各位的閱讀,以上就是“PostgreSQL中GetSnapshotData的處理過程是什么”的內容了,經過本文的學習后,相信大家對PostgreSQL中GetSnapshotData的處理過程是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

巴林左旗| 延吉市| 于都县| 新乡市| 将乐县| 榆树市| 保亭| 昌邑市| 浦北县| 黑山县| 桑日县| 翁牛特旗| 怀集县| 买车| 绥芬河市| 镇坪县| 河东区| 铜山县| 宜昌市| 开远市| 无极县| 高邑县| 崇文区| 互助| 唐河县| 磐安县| 依安县| 灯塔市| 山东| 墨玉县| 雷山县| 安阳市| 金门县| 聂拉木县| 峨眉山市| 平江县| 朝阳县| 筠连县| 泰和县| 阳江市| 剑川县|