美文网首页PostgreSQL
PostgreSQL 源码解读(121)- MVCC#6(获取事

PostgreSQL 源码解读(121)- MVCC#6(获取事

作者: EthanHe | 来源:发表于2020-07-17 21:24 被阅读0次

    本节介绍了PostgreSQL获取事务号XID的逻辑,主要解析了函数AssignTransactionId->GetNewTransactionId的实现逻辑。

    在GetNewTransactionId函数中,检查是否可以安全的分配XID.
    这可以防止由于XID wraparound而导致的灾难性数据丢失.
    基本规则是:
    如果超过了xidVacLimit,开始尝试强制autovacuum循环.
    如果超过了xidWarnLimit,开始发出警告.
    如果超过了xidStopLimit,拒绝执行事务,直至以单用户模式运行.
    (这为DBA提供了一个逃生通道,使他们能够通过数据库早期的安全性检测,进入数据库进行维护)

    ShmemVariableCache->xidVacLimit --> 200,000,561 --> 大于等于该值,触发自动vacuum
    ShmemVariableCache->xidWarnLimit --> 2,136,484,208 --> 大于等于该值,系统报警
    ShmemVariableCache->xidStopLimit --> 2,146,484,208 --> 大于等于该值,系统不允许执行事务,使用单用户模式处理

    一、数据结构

    TransactionState
    事务状态结构体

    /*
     *  transaction states - transaction state from server perspective
     *  事务状态枚举 - 服务器视角的事务状态
     */
    typedef enum TransState
    {
        TRANS_DEFAULT,              /* idle 空闲 */
        TRANS_START,                /* transaction starting 事务启动 */
        TRANS_INPROGRESS,           /* inside a valid transaction 进行中 */
        TRANS_COMMIT,               /* commit in progress 提交中 */
        TRANS_ABORT,                /* abort in progress 回滚中 */
        TRANS_PREPARE               /* prepare in progress 准备中 */
    } TransState;
    
    /*
     *  transaction block states - transaction state of client queries
     *  事务块状态 - 客户端查询的事务状态
     *
     * Note: the subtransaction states are used only for non-topmost
     * transactions; the others appear only in the topmost transaction.
     * 注意:subtransaction只用于非顶层事务;其他字段用于顶层事务.
     */
    typedef enum TBlockState
    {
        /* not-in-transaction-block states 未进入事务块状态 */
        TBLOCK_DEFAULT,             /* idle 空闲  */
        TBLOCK_STARTED,             /* running single-query transaction 单个查询事务 */
    
        /* transaction block states 事务块状态 */
        TBLOCK_BEGIN,               /* starting transaction block 开始事务块 */
        TBLOCK_INPROGRESS,          /* live transaction 进行中 */
        TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN 隐式事务,进行中 */
        TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker 并行worker中的事务,进行中 */
        TBLOCK_END,                 /* COMMIT received 接收到COMMIT */
        TBLOCK_ABORT,               /* failed xact, awaiting ROLLBACK 失败,等待ROLLBACK */
        TBLOCK_ABORT_END,           /* failed xact, ROLLBACK received 失败,已接收ROLLBACK */
        TBLOCK_ABORT_PENDING,       /* live xact, ROLLBACK received 进行中,接收到ROLLBACK */
        TBLOCK_PREPARE,             /* live xact, PREPARE received 进行中,接收到PREPARE */
    
        /* subtransaction states 子事务状态 */
        TBLOCK_SUBBEGIN,            /* starting a subtransaction 开启 */
        TBLOCK_SUBINPROGRESS,       /* live subtransaction 进行中 */
        TBLOCK_SUBRELEASE,          /* RELEASE received 接收到RELEASE */
        TBLOCK_SUBCOMMIT,           /* COMMIT received while TBLOCK_SUBINPROGRESS 进行中,接收到COMMIT */
        TBLOCK_SUBABORT,            /* failed subxact, awaiting ROLLBACK 失败,等待ROLLBACK */
        TBLOCK_SUBABORT_END,        /* failed subxact, ROLLBACK received 失败,已接收ROLLBACK */
        TBLOCK_SUBABORT_PENDING,    /* live subxact, ROLLBACK received 进行中,接收到ROLLBACK */
        TBLOCK_SUBRESTART,          /* live subxact, ROLLBACK TO received 进行中,接收到ROLLBACK TO */
        TBLOCK_SUBABORT_RESTART     /* failed subxact, ROLLBACK TO received 失败,已接收ROLLBACK TO */
    } TBlockState;
    
    /*
     *  transaction state structure
     *  事务状态结构体
     */
    typedef struct TransactionStateData
    {
        //事务ID
        TransactionId transactionId;    /* my XID, or Invalid if none */
        //子事务ID
        SubTransactionId subTransactionId;  /* my subxact ID */
        //保存点名称
        char       *name;           /* savepoint name, if any */
        //保存点级别
        int         savepointLevel; /* savepoint level */
        //低级别的事务状态
        TransState  state;          /* low-level state */
        //高级别的事务状态
        TBlockState blockState;     /* high-level state */
        //事务嵌套深度
        int         nestingLevel;   /* transaction nesting depth */
        //GUC上下文嵌套深度
        int         gucNestLevel;   /* GUC context nesting depth */
        //事务生命周期上下文
        MemoryContext curTransactionContext;    /* my xact-lifetime context */
        //查询资源
        ResourceOwner curTransactionOwner;  /* my query resources */
        //按XID顺序保存的已提交的子事务ID
        TransactionId *childXids;   /* subcommitted child XIDs, in XID order */
        //childXids数组大小
        int         nChildXids;     /* # of subcommitted child XIDs */
        //分配的childXids数组空间
        int         maxChildXids;   /* allocated size of childXids[] */
        //上一个CurrentUserId
        Oid         prevUser;       /* previous CurrentUserId setting */
        //上一个SecurityRestrictionContext
        int         prevSecContext; /* previous SecurityRestrictionContext */
        //上一事务是否只读?
        bool        prevXactReadOnly;   /* entry-time xact r/o state */
        //是否处于Recovery?
        bool        startedInRecovery;  /* did we start in recovery? */
        //XID是否已保存在WAL Record中?
        bool        didLogXid;      /* has xid been included in WAL record? */
        //Enter/ExitParallelMode计数器
        int         parallelModeLevel;  /* Enter/ExitParallelMode counter */
        //父事务状态
        struct TransactionStateData *parent;    /* back link to parent */
    } TransactionStateData;
    
    //结构体指针
    typedef TransactionStateData *TransactionState;
    
    

    ShmemVariableCache
    VariableCache是共享内存中的一种数据结构,用于跟踪OID和XID分配状态。
    ShmemVariableCache-->共享内存中的指针

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

    二、源码解读

    AssignTransactionId函数,给定的TransactionState分配一个新的持久化事务号XID,在此函数调用前,不会为事务分配XIDs.GetNewTransactionId是获取事务ID实际执行的地方,该函数从共享内存变量ShmemVariableCache中获取nextXid,+1后作为新的XID.

    /*
     * Allocate the next XID for a new transaction or subtransaction.
     * 为新事务或新子事务分配XID
     *
     * The new XID is also stored into MyPgXact before returning.
     * 在返回前,XID会存储在全局变量MyPgXact中
     *
     * Note: when this is called, we are actually already inside a valid
     * transaction, since XIDs are now not allocated until the transaction
     * does something.  So it is safe to do a database lookup if we want to
     * issue a warning about XID wrap.
     * 注意:在该函数调用时,我们实际上已在一个有效的事务中,因为XIDs在事务不做些事情前不会分配.
     * 因此,如果我们想发出关于XID wrap回卷的警告,那么进行数据库查找是安全的。
     */
    TransactionId
    GetNewTransactionId(bool isSubXact)
    {
        TransactionId xid;
    
        /*
         * Workers synchronize transaction state at the beginning of each parallel
         * operation, so we can't account for new XIDs after that point.
         * 在每个并行操作前,Parallel Workers同步事务状态,
         *   因此我们不能在这时候请求XIDs
         */
        if (IsInParallelMode())
            elog(ERROR, "cannot assign TransactionIds during a parallel operation");
    
        /*
         * During bootstrap initialization, we return the special bootstrap
         * transaction id.
         * 在宇宙初启时,返回特别的bootstrap事务ID
         */
        if (IsBootstrapProcessingMode())
        {
            Assert(!isSubXact);
            MyPgXact->xid = BootstrapTransactionId;
            return BootstrapTransactionId;//--> 1
        }
    
        /* safety check, we should never get this far in a HS standby */
        * 安全检查
        if (RecoveryInProgress())
            elog(ERROR, "cannot assign TransactionIds during recovery");
    
        LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
    
        //从共享内存中获取下一个XID
        xid = ShmemVariableCache->nextXid;
    
        /*----------
         * Check to see if it's safe to assign another XID.  This protects against
         * catastrophic data loss due to XID wraparound.  The basic rules are:
         * 检查是否可以安全的分配另外一个XID.
         * 这可以防止由于XID wraparound而导致的灾难性数据丢失.
         * 基本规则是:
         *
         * If we're past xidVacLimit, start trying to force autovacuum cycles.
         * If we're past xidWarnLimit, start issuing warnings.
         * If we're past xidStopLimit, refuse to execute transactions, unless
         * we are running in single-user mode (which gives an escape hatch
         * to the DBA who somehow got past the earlier defenses).
         * 如果超过了xidVacLimit,开始尝试强制autovacuum循环.
         * 如果超过了xidWarnLimit,开始发出警告.
         * 如果超过了xidStopLimit,拒绝执行事务,直至以单用户模式运行.
         * (这为DBA提供了一个逃生通道,使他们能够通过数据库早期的安全性检测)
         *
         * Note that this coding also appears in GetNewMultiXactId.
         * 注意这部分代码在GetNewMultiXactId中也会出现.
         *----------
         */
        //TransactionIdFollowsOrEquals --> is id1 logically >= id2?
        if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit))
        {
            //xid >= ShmemVariableCache->xidVacLimit
            /*
             * For safety's sake, we release XidGenLock while sending signals,
             * warnings, etc.  This is not so much because we care about
             * preserving concurrency in this situation, as to avoid any
             * possibility of deadlock while doing get_database_name(). First,
             * copy all the shared values we'll need in this path.
             * 为了安全起见,我们在发送信号、警告等时释放XidGenLock。
             * 这并不是因为我们关心在这种情况下并发性,
             *   而是因为在执行get_database_name()时要避免出现死锁
             */
            //获取相关XID
            TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;
            TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;
            TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;
            Oid         oldest_datoid = ShmemVariableCache->oldestXidDB;
    
            LWLockRelease(XidGenLock);
    
            /*
             * To avoid swamping the postmaster with signals, we issue the autovac
             * request only once per 64K transaction starts.  This still gives
             * plenty of chances before we get into real trouble.
             * 为了避免信号淹没postmaster,我们每64K事务开始时只发出一次autovac请求。
             * 在我们陷入真正的麻烦之前,这仍然给了我们很多解决问题的机会。
             */
            if (IsUnderPostmaster && (xid % 65536) == 0)
                //每隔64K发一次
                SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
    
            if (IsUnderPostmaster &&
                TransactionIdFollowsOrEquals(xid, xidStopLimit))
            {
                //xid >= ShmemVariableCache->xidStopLimit
                char       *oldest_datname = get_database_name(oldest_datoid);
    
                /* complain even if that DB has disappeared */
                //就算DB已消失,也要不停的警告:(
                if (oldest_datname)
                    ereport(ERROR,
                            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                             errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
                                    oldest_datname),
                             errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
                                     "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
                else
                    ereport(ERROR,
                            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                             errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
                                    oldest_datoid),
                             errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
                                     "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
            }
            else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
            {
                ////xid >= ShmemVariableCache->xidWarnLimit
                char       *oldest_datname = get_database_name(oldest_datoid);
    
                /* complain even if that DB has disappeared */
                if (oldest_datname)
                    ereport(WARNING,
                            (errmsg("database \"%s\" must be vacuumed within %u transactions",
                                    oldest_datname,
                                    xidWrapLimit - xid),
                             errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                                     "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
                else
                    ereport(WARNING,
                            (errmsg("database with OID %u must be vacuumed within %u transactions",
                                    oldest_datoid,
                                    xidWrapLimit - xid),
                             errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                                     "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
            }
    
            /* Re-acquire lock and start over */
            //重新获取锁并启动
            LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
            xid = ShmemVariableCache->nextXid;
        }
    
        /*
         * If we are allocating the first XID of a new page of the commit log,
         * zero out that commit-log page before returning. We must do this while
         * holding XidGenLock, else another xact could acquire and commit a later
         * XID before we zero the page.  Fortunately, a page of the commit log
         * holds 32K or more transactions, so we don't have to do this very often.
         * 如果在clog的新page中分配第一个XID,返回前初始化clog page.
         * 必须在持有XidGenLock锁时执行这个操作,否则的话,
         *   其他事务可能会请求该锁并在初始化page前提交了一个新事务.
         * 幸运的是,提交日志的一个页面包含32K或更多的事务,所以我们不需要经常这样做。
         *
         * Extend pg_subtrans and pg_commit_ts too.
         * 同时扩展pg_subtrans和pg_commit_ts
         */
        ExtendCLOG(xid);
        ExtendCommitTs(xid);
        ExtendSUBTRANS(xid);
    
        /*
         * Now advance the nextXid counter.  This must not happen until after we
         * have successfully completed ExtendCLOG() --- if that routine fails, we
         * want the next incoming transaction to try it again.  We cannot assign
         * more XIDs until there is CLOG space for them.
         * 现在可以更新nextXid计数器了.
         * 这必须在我们成功完成ExtendCLOG()之后才能执行——如果该例程失败,
         *   我们希望下一个进入的事务再次尝试。
         * 不能分配更多的xid,除非有空闲的CLOG空间。
         */
        //ShmemVariableCache->nextXid ++
        //if ((ShmemVariableCache->nextXid) < FirstNormalTransactionId) 
        //   (ShmemVariableCache->nextXid) = FirstNormalTransactionId; 
        TransactionIdAdvance(ShmemVariableCache->nextXid);
    
        /*
         * We must store the new XID into the shared ProcArray before releasing
         * XidGenLock.  This ensures that every active XID older than
         * latestCompletedXid is present in the ProcArray, which is essential for
         * correct OldestXmin tracking; see src/backend/access/transam/README.
         * 在释放XidGenLock前,存储新的XID到共享数据结构ProcArray中.
         * 这可以确保每一个活动的比latestCompletedXid旧的XID都会出现在ProcArray中,
         *   这样可以减少OldestXmin的跟踪,请查看src/backend/access/transam/README.
         * 
         * Note that readers of PGXACT xid fields should be careful to fetch the
         * value only once, rather than assume they can read a value multiple
         * times and get the same answer each time.  Note we are assuming that
         * TransactionId and int fetch/store are atomic.
         * 要注意的是读取PGXACT.xid字段时小心只提取一次,
         *   而不是假定可以多次读取该值而认为每次返回的值都一样.
         * 同时我们假定TransactionId和int 提取/写入是原子的.
         * 
         * The same comments apply to the subxact xid count and overflow fields.
         * 对于subxact xid计数器和溢出字段,参见上述注释.
         *
         * Use of a write barrier prevents dangerous code rearrangement in this
         * function; other backends could otherwise e.g. be examining my subxids
         * info concurrently, and we don't want them to see an invalid
         * intermediate state, such as an incremented nxids before the array entry
         * is filled.
         * 在该函数中,进入堡垒进行写可以防止危险的事情出现.
         * 否则其他后台进程可能会比如同步检查本进程的subxids信息,但我们不希望它们看到无效的中间状态,
         *   例如,在数组条目被填充之前增加nxid。
         *
         * Other processes that read nxids should do so before reading xids
         * elements with a pg_read_barrier() in between, so that they can be sure
         * not to read an uninitialized array element; see
         * src/backend/storage/lmgr/README.barrier.
         * 其他读取nxids的进程应在使用pg_read_barrier()函数读取xids条目前执行相关操作,
         *   这样可以确保它们不会读取到未经初始化的数组条目,查看src/backend/storage/lmgr/README.barrier说明.
         *
         * If there's no room to fit a subtransaction XID into PGPROC, set the
         * cache-overflowed flag instead.  This forces readers to look in
         * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a
         * race-condition window, in that the new XID will not appear as running
         * until its parent link has been placed into pg_subtrans. However, that
         * will happen before anyone could possibly have a reason to inquire about
         * the status of the XID, so it seems OK.  (Snapshots taken during this
         * window *will* include the parent XID, so they will deliver the correct
         * answer later on when someone does have a reason to inquire.)
         * 如果没有空间将子事务XID放入PGPROC中,则设置cache-overflow标志。
         * 这可以强制readers查看pg_subtrans以将子事务xid映射到顶层xid。
         * 有一个race-condition窗口,在它的父链接被放入pg_subtrans之前,新的XID不会显示为正在运行.
         * 然而,在都有可能有理由查询XID的状态之前,这种情况就会发生,所以看起来是可以的。
         * (在这个窗口中拍摄的快照*将*包含父XID,因此当稍后有进程查询时,它们将提供正确的答案。)
         */
        if (!isSubXact)
            MyPgXact->xid = xid;    /* LWLockRelease acts as barrier */
        else
        {
            int         nxids = MyPgXact->nxids;
    
            if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
            {
                MyProc->subxids.xids[nxids] = xid;
                pg_write_barrier();
                MyPgXact->nxids = nxids + 1;
            }
            else
                MyPgXact->overflowed = true;
        }
        //释放锁
        LWLockRelease(XidGenLock);
    
        return xid;
    }
     
    

    三、跟踪分析

    执行txid_current,触发函数调用

    14:26:26 (xdb@[local]:5432)testdb=#  begin;
    BEGIN
    14:26:50 (xdb@[local]:5432)testdb=#* select txid_current_if_assigned();
     txid_current_if_assigned 
    --------------------------
                             
    (1 row)
    
    14:26:55 (xdb@[local]:5432)testdb=#* select txid_current();
    
    

    启动gdb,设置断点

    (gdb) b GetNewTransactionId
    Breakpoint 6 at 0x545e80: file varsup.c, line 56.
    (gdb) c
    Continuing.
    
    Breakpoint 6, GetNewTransactionId (isSubXact=false) at varsup.c:56
    56      if (IsInParallelMode())
    (gdb) 
    

    查看调用栈

    (gdb) bt
    #0  GetNewTransactionId (isSubXact=false) at varsup.c:56
    #1  0x0000000000546bd9 in AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) at xact.c:557
    #2  0x000000000054693d in GetTopTransactionId () at xact.c:392
    #3  0x00000000009fe1f3 in txid_current (fcinfo=0x254c7e0) at txid.c:443
    #4  0x00000000006cfebd in ExecInterpExpr (state=0x254c6f8, econtext=0x254c3e8, isnull=0x7ffe3d4a31f7)
        at execExprInterp.c:654
    #5  0x00000000006d1ac6 in ExecInterpExprStillValid (state=0x254c6f8, econtext=0x254c3e8, isNull=0x7ffe3d4a31f7)
        at execExprInterp.c:1786
    #6  0x00000000007140dd in ExecEvalExprSwitchContext (state=0x254c6f8, econtext=0x254c3e8, isNull=0x7ffe3d4a31f7)
        at ../../../src/include/executor/executor.h:303
    #7  0x000000000071414b in ExecProject (projInfo=0x254c6f0) at ../../../src/include/executor/executor.h:337
    #8  0x0000000000714323 in ExecResult (pstate=0x254c2d0) at nodeResult.c:136
    #9  0x00000000006e4c30 in ExecProcNodeFirst (node=0x254c2d0) at execProcnode.c:445
    #10 0x00000000006d9974 in ExecProcNode (node=0x254c2d0) at ../../../src/include/executor/executor.h:237
    #11 0x00000000006dc22d in ExecutePlan (estate=0x254c0b8, planstate=0x254c2d0, use_parallel_mode=false, 
        operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x24ccf10, 
        execute_once=true) at execMain.c:1723
    #12 0x00000000006d9f5c in standard_ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, 
        execute_once=true) at execMain.c:364
    #13 0x00000000006d9d7f in ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, execute_once=true)
        at execMain.c:307
    #14 0x00000000008ccf5a in PortalRunSelect (portal=0x250c860, forward=true, count=0, dest=0x24ccf10) at pquery.c:932
    #15 0x00000000008ccbf3 in PortalRun (portal=0x250c860, count=9223372036854775807, isTopLevel=true, run_once=true, 
        dest=0x24ccf10, altdest=0x24ccf10, completionTag=0x7ffe3d4a3570 "") at pquery.c:773
    #16 0x00000000008c6b1e in exec_simple_query (query_string=0x24a6ec8 "select txid_current();") at postgres.c:1145
    #17 0x00000000008cae70 in PostgresMain (argc=1, argv=0x24d2dc8, dbname=0x24d2c30 "testdb", username=0x24a3ba8 "xdb")
        at postgres.c:4182
    ---Type <return> to continue, or q <return> to quit---
    #18 0x000000000082642b in BackendRun (port=0x24c8c00) at postmaster.c:4361
    #19 0x0000000000825b8f in BackendStartup (port=0x24c8c00) at postmaster.c:4033
    #20 0x0000000000821f1c in ServerLoop () at postmaster.c:1706
    #21 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x24a1b60) at postmaster.c:1379
    #22 0x00000000007488ef in main (argc=1, argv=0x24a1b60) at main.c:228
    (gdb) 
    

    获取XidGenLock锁

    (gdb) n
    63      if (IsBootstrapProcessingMode())
    (gdb) 
    71      if (RecoveryInProgress())
    (gdb) 
    74      LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
    (gdb) 
    

    获取共享内存变量ShmemVariableCache->nextXid --> 2409

    (gdb) 
    76      xid = ShmemVariableCache->nextXid;
    (gdb) 
    91      if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit))
    (gdb) p *ShmemVariableCache
    $16 = {nextOid = 42628, oidCount = 8191, nextXid = 2409, oldestXid = 561, xidVacLimit = 200000561, 
      xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, 
      oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408, oldestClogXid = 561}
    (gdb) 
    

    扩展clog

    (gdb) n
    171     ExtendCLOG(xid);
    (gdb) 
    172     ExtendCommitTs(xid);
    (gdb) 
    173     ExtendSUBTRANS(xid);
    (gdb) 
    181     TransactionIdAdvance(ShmemVariableCache->nextXid);
    (gdb) 
    

    ShmemVariableCache->nextXid++ --> 2410

    (gdb) p ShmemVariableCache->nextXid
    $17 = 2410
    

    获取进程和事务信息

    (gdb) n
    223         volatile PGXACT *mypgxact = MyPgXact;
    (gdb) 
    225         if (!isSubXact)
    (gdb) 
    226             mypgxact->xid = xid;
    (gdb) 
    

    释放锁XidGenLock

    (gdb) 
    241     LWLockRelease(XidGenLock);
    (gdb) p *ShmemVariableCache
    $18 = {nextOid = 42628, oidCount = 8191, nextXid = 2410, oldestXid = 561, xidVacLimit = 200000561, 
      xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, 
      oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408, oldestClogXid = 561}
    

    返回xid(2409),完成调用.

    (gdb) n
    243     return xid;
    (gdb) 
    244 }
    (gdb) 
    AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) at xact.c:558
    558     if (!isSubXact)
    (gdb) 
    

    DONE!

    四、参考资料

    PG Source Code

    相关文章

      网友评论

        本文标题:PostgreSQL 源码解读(121)- MVCC#6(获取事

        本文链接:https://www.haomeiwen.com/subject/ceinhktx.html