美文网首页PostgreSQL
PostgreSQL 源码解读(118)- MVCC#3(Tup

PostgreSQL 源码解读(118)- MVCC#3(Tup

作者: EthanHe | 来源:发表于2020-07-14 09:34 被阅读0次

    本节介绍了PostgreSQL判断元组可见性的实现逻辑,重点解析了HeapTupleSatisfiesMVCC函数的处理过程。

    一、数据结构

    宏定义

    //事务ID,无符号32位整型
    typedef uint32 TransactionId;
    
    /* ----------------
     *      Special transaction ID values
     *      系统保留的事务ID
     *
     * BootstrapTransactionId is the XID for "bootstrap" operations, and
     * FrozenTransactionId is used for very old tuples.  Both should
     * always be considered valid.
     *
     * FirstNormalTransactionId is the first "normal" transaction id.
     * Note: if you need to change it, you must change pg_class.h as well.
     * ----------------
     */
    #define InvalidTransactionId        ((TransactionId) 0)
    #define BootstrapTransactionId      ((TransactionId) 1)
    #define FrozenTransactionId         ((TransactionId) 2)
    #define FirstNormalTransactionId    ((TransactionId) 3)
    #define MaxTransactionId            ((TransactionId) 0xFFFFFFFF)
    
    /* ----------------
     *      transaction ID manipulation macros
     * ----------------
     */
    #define TransactionIdIsValid(xid)       ((xid) != InvalidTransactionId)
    #define TransactionIdIsNormal(xid)      ((xid) >= FirstNormalTransactionId)
    #define TransactionIdEquals(id1, id2)   ((id1) == (id2))
    #define TransactionIdStore(xid, dest)   (*(dest) = (xid))
    #define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
    
    /* advance a transaction ID variable, handling wraparound correctly */
    #define TransactionIdAdvance(dest)  \
        do { \
            (dest)++; \
            if ((dest) < FirstNormalTransactionId) \
                (dest) = FirstNormalTransactionId; \
        } while(0)
    
    /* back up a transaction ID variable, handling wraparound correctly */
    #define TransactionIdRetreat(dest)  \
        do { \
            (dest)--; \
        } while ((dest) < FirstNormalTransactionId)
    
    /* compare two XIDs already known to be normal; this is a macro for speed */
    //比较两个已知是常规事务的XIDs;宏定义是为了性能考虑.
    #define NormalTransactionIdPrecedes(id1, id2) \
        (AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
        (int32) ((id1) - (id2)) < 0)
    
    /* compare two XIDs already known to be normal; this is a macro for speed */
    #define NormalTransactionIdFollows(id1, id2) \
        (AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
        (int32) ((id1) - (id2)) > 0)
    
    /*
     * HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid
     * originally used to insert the tuple.  However, the tuple might actually
     * be frozen (via HeapTupleHeaderSetXminFrozen) in which case the tuple's xmin
     * is visible to every snapshot.  Prior to PostgreSQL 9.4, we actually changed
     * the xmin to FrozenTransactionId, and that value may still be encountered
     * on disk.
     * HeapTupleHeaderGetRawXmin返回“原始”xmin字段,这是最初用于插入元组的xid。
     * 但是,实际上元组实可能被冻结(通过HeapTupleHeaderSetXminFrozen),
     *   在这种情况下,元组可对每个快照都可见。
     * 在PostgreSQL 9.4之前,我们实际上将xmin更改为FrozenTransactionId,该值仍然可能在磁盘上遇到。
     */
    #define HeapTupleHeaderGetRawXmin(tup) \
    ( \
        (tup)->t_choice.t_heap.t_xmin \
    )
    
    #define HeapTupleHeaderXminCommitted(tup) \
    ( \
        ((tup)->t_infomask & HEAP_XMIN_COMMITTED) != 0 \
    )
    
    /*
     * HeapTupleHeaderGetRawXmax gets you the raw Xmax field.  To find out the Xid
     * that updated a tuple, you might need to resolve the MultiXactId if certain
     * bits are set.  HeapTupleHeaderGetUpdateXid checks those bits and takes care
     * to resolve the MultiXactId if necessary.  This might involve multixact I/O,
     * so it should only be used if absolutely necessary.
     * HeapTupleHeaderGetRawXmax返回原始的Xmax字段.
     * 搜索更新元组的事务Xid,如设定了特定的标记位则可能需要解决MultiXactId.
     * HeapTupleHeaderGetUpdateXid检查这些标记位并在需要时解决MultiXactId问题.
     * 这可能会引起multixact I/O,如非必要不要使用.
     */
    #define HeapTupleHeaderGetUpdateXid(tup) \
    ( \
        (!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
         ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
         !((tup)->t_infomask & HEAP_XMAX_LOCK_ONLY)) ? \
            HeapTupleGetUpdateXid(tup) \
        : \
            HeapTupleHeaderGetRawXmax(tup) \
    )
     
    #define HeapTupleHeaderGetRawXmax(tup) \
    ( \
        (tup)->t_choice.t_heap.t_xmax \
    )
     
    
    /*
     * A tuple is only locked (i.e. not updated by its Xmax) if the
     * HEAP_XMAX_LOCK_ONLY bit is set; or, for pg_upgrade's sake, if the Xmax is
     * not a multi and the EXCL_LOCK bit is set.
     * 如HEAP_XMAX_LOCK_ONLY标记位设置,则表示仅仅只是锁定了tuple(比如并非更新了Xmax字段).
     * 或者,对于pg_upgrade升级,如Xmax并非HEAP_XMAX_IS_MULTI而且EXCL_LOCK标记位设置,也是表达这个意思.
     *
     * See also HeapTupleHeaderIsOnlyLocked, which also checks for a possible
     * aborted updater transaction.
     * 可同时参考HeapTupleHeaderIsOnlyLocked,该函数也检查了可能已回滚的更新事务.
     *
     * Beware of multiple evaluations of the argument.
     * 注意对参数的多种分析.
     */
    #define HEAP_XMAX_IS_LOCKED_ONLY(infomask) \
        (((infomask) & HEAP_XMAX_LOCK_ONLY) || \
         (((infomask) & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK))
    
    

    二、源码解读

    全局变量snapshot->satisfies函数在初始化该全局变量已设置为HeapTupleSatisfiesMVCC.

    static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
    

    HeapTupleSatisfiesMVCC -- 如Tuple对于给定的MVCC快照可见,则返回T

    /*
     * HeapTupleSatisfiesMVCC
     *      True iff heap tuple is valid for the given MVCC snapshot.
     * HeapTupleSatisfiesMVCC -- 如Tuple对于给定的MVCC快照可见,则返回T
     *
     *  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
     * 但不包括:
     *      快照显示仍在进行中的事务.
     *      快照之后才启动的事务.
     *     
     *
     * Notice that here, we will not update the tuple status hint bits if the
     * inserting/deleting transaction is still running according to our snapshot,
     * even if in reality it's committed or aborted by now.  This is intentional.
     * Checking the true transaction state would require access to high-traffic
     * shared data structures, creating contention we'd rather do without, and it
     * would not change the result of our visibility check anyway.  The hint bits
     * will be updated by the first visitor that has a snapshot new enough to see
     * the inserting/deleting transaction as done.  In the meantime, the cost of
     * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
     * call will need to run TransactionIdIsCurrentTransactionId in addition to
     * XidInMVCCSnapshot (but it would have to do the latter anyway).  In the old
     * coding where we tried to set the hint bits as soon as possible, we instead
     * did TransactionIdIsInProgress in each call --- to no avail, as long as the
     * inserting/deleting transaction was still running --- which was more cycles
     * and more contention on the PGXACT array.
     * 在这里需要注意的是,如果插入/删除事务仍然在快照范围内运行,
     *   那么我们将不会更新元组状态提示位,即使它现在已经提交或中止。这是做法内部约定的.
     * 检查真正的事务状态需要访问高频访问的共享数据结构,
     *   这就产生了我们不希望看到的争用,而且它也不会改变可见性检查的结果。
     * 第一个访问者将更新提示位,该访问者的快照足够新,可以看到插入/删除事务已经完成。
     * 与此同时,不设置提示位的代价是,除了XidInMVCCSnapshot之外,
     *   每个HeapTupleSatisfiesMVCC调用都需要运行TransactionIdIsCurrentTransactionId
     *   (无论如何都必须执行后者)。
     * 在旧的编码中,我们试图尽快地设置提示位,
     *   而不是在每次调用中执行TransactionIdIsInProgress——
     *      只要插入/删除事务仍在运行,这些工作看起来都是徒劳的
     *   ——导致PGXACT数组上更多的循环和争用。
     */
    bool
    HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
                           Buffer buffer)
    {
        HeapTupleHeader tuple = htup->t_data;//Tuple
    
        Assert(ItemPointerIsValid(&htup->t_self));
        Assert(htup->t_tableOid != InvalidOid);
    
        if (!HeapTupleHeaderXminCommitted(tuple))
        {
            //A.xmin事务未提交(HEAP_XMIN_COMMITTED标记未设置)
            if (HeapTupleHeaderXminInvalid(tuple))
                //xmin = 0,此tuple不可见
                return false;
    
            /* Used by pre-9.0 binary upgrades */
            //用于9.0前二进制可执行文件的升级,HEAP_MOVED_OFF & HEAP_MOVED_IN已不再使用
            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
    
                if (TransactionIdIsCurrentTransactionId(xvac))
                    return false;
                if (!XidInMVCCSnapshot(xvac, snapshot))
                {
                    if (TransactionIdDidCommit(xvac))
                    {
                        SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                        return false;
                    }
                    SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                InvalidTransactionId);
                }
            }
            /* Used by pre-9.0 binary upgrades */
            //同上
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
    
                if (!TransactionIdIsCurrentTransactionId(xvac))
                {
                    if (XidInMVCCSnapshot(xvac, snapshot))
                        return false;
                    if (TransactionIdDidCommit(xvac))
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    InvalidTransactionId);
                    else
                    {
                        SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                        return false;
                    }
                }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
            {
                //A.xmin事务未提交
                //A1.xmin对应的事务是当前事务
                if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
                    //A1-1.tuple.cmin >= 快照的cid,说明插入在扫描开始前,返回F
                    return false;   /* inserted after scan started */
    
                if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid */
                    //A1-2.xmax无效,返回T
                    return true;
    
                if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))    /* not deleter */
                    //A1-3.XMAX仅仅只是锁定而已,返回T
                    return true;
    
                if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
                {
                    //xmax is a MultiXactId 
                    TransactionId xmax;
                    //获取更新的Xid
                    xmax = HeapTupleGetUpdateXid(tuple);
    
                    /* not LOCKED_ONLY, so it has to have an xmax */
                    //验证xmax是有效的
                    Assert(TransactionIdIsValid(xmax));
    
                    /* updating subtransaction must have aborted */
                    //执行更新的子事务必须已回滚(why?因为前提是xmin事务是未提交的,因此xmax必须已回滚)
                    if (!TransactionIdIsCurrentTransactionId(xmax))
                        //正在执行删除的事务不是本事务,返回T
                        return true;
                    else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                        //本事务执行删除:删除命令在快照之后,返回T
                        return true;    /* updated after scan started */
                    else
                        //本事务执行删除:删除命令在快照之前,返回F
                        return false;   /* updated before scan started */
                }
    
                if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
                {
                    //A1-4.tuple.xmax对应的事务不是本事务
                    /* deleting subtransaction must have aborted */
                    //删除子事务已终止(why?因为前提是xmin事务是未提交的,因此xmax必须已回滚)
                    SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                InvalidTransactionId);
                    return true;
                }
    
                if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                    //A1-5.删除在快照之后,返回T
                    return true;    /* deleted after scan started */
                else
                    //A1-6.删在快照之前,返回F
                    return false;   /* deleted before scan started */
            }
            else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
                //A.xmin事务未提交;
                //A2.xmin在当前快照中(非本事务) --> 不可见
                return false;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
                //A.xmin事务未提交;
                //A3.查询事务日志clog,Xmin事务已提交,则设置标志位,可见判断逻辑在后续执行
                SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                            HeapTupleHeaderGetRawXmin(tuple));
            else
            {
                /* it must have aborted or crashed */
                //A4.以上各种情况都不符合,事务回滚或者在执行事务过程中crash了
                //设置标志位,返回F
                SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                            InvalidTransactionId);
                return false;
            }
        }
        else
        {
            /* xmin is committed, but maybe not according to our snapshot */
            //B.xmin事务已提交,但可能不属于该快照
            if (!HeapTupleHeaderXminFrozen(tuple) &&
                XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
                return false;       /* treat as still in progress */
        }
    
        /* by here, the inserting transaction has committed */
        //C.Xmin事务已提交
        if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid or aborted */
            //C1.xmax无效(HEAP_XMAX_INVALID),则返回T
            return true;
    
        if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
            //C2.xmax仅仅只是lock而已,返回T
            return true;
    
        if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
        {
            //xmax is a MultiXactId 
            TransactionId xmax;
    
            /* already checked above */
            //再次检查
            Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
            //获取xmax
            xmax = HeapTupleGetUpdateXid(tuple);
    
            /* not LOCKED_ONLY, so it has to have an xmax */
            Assert(TransactionIdIsValid(xmax));
    
            if (TransactionIdIsCurrentTransactionId(xmax))
            {
                //xmax为当前事务id
                if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                    //快照之后执行删除,返回T
                    return true;    /* deleted after scan started */
                else
                    //快照之前执行删除,返回F
                    return false;   /* deleted before scan started */
            }
            if (XidInMVCCSnapshot(xmax, snapshot))
                //xmax仍在进行中,返回T
                return true;
            if (TransactionIdDidCommit(xmax))
                //xmax事务通过查询clog日志,已确认提交,则返回F
                return false;       /* updating transaction committed */
            /* it must have aborted or crashed */
            //xmax回滚或者执行期间crash,返回T
            return true;
        }
    
        if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
        {
            //C3.xmax未提交
            if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
            {
                //C3-1:本事务
                if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                    return true;    /* deleted after scan started */
                else
                    return false;   /* deleted before scan started */
            }
    
            if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
                //C3-2:非本事务
                //xmax事务仍在进行中
                return true;
    
            if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
            {
                /* it must have aborted or crashed */
                //C3-3查询clog日志,xmax事务未提交(回滚或crash),设置标记
                SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                            InvalidTransactionId);
                //可见
                return true;
            }
    
            /* xmax transaction committed */
            //以上判断均不成立,则可认为事务已提交,设置标记
            SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                        HeapTupleHeaderGetRawXmax(tuple));
        }
        else
        {
            /* xmax is committed, but maybe not according to our snapshot */
            //C4.xmax已提交,但快照指示该事务仍在进行中
            if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
                //仍在处理中,返回T
                return true;        /* treat as still in progress */
        }
    
        /* xmax transaction committed */
        //C5.xmax已提交,返回F
        return false;
    }
    
    

    三、跟踪分析

    创建数据表,插入数据,删除其中一行数据,提交,更新其中一条数据,不提交,执行查询.
    session 1

    11:55:00 (xdb@[local]:5432)testdb=# drop table t_mvcc2;
    ERROR:  table "t_mvcc2" does not exist
    11:55:01 (xdb@[local]:5432)testdb=# create table t_mvcc2(c1 int not null,c2  varchar(40),c3 varchar(40));
    CREATE TABLE
    11:55:02 (xdb@[local]:5432)testdb=# 
    11:55:02 (xdb@[local]:5432)testdb=# insert into t_mvcc2(c1,c2,c3) values(1,'C2-1','C3-1');
    INSERT 0 1
    11:55:02 (xdb@[local]:5432)testdb=# insert into t_mvcc2(c1,c2,c3) values(2,'C2-2','C3-2');
    INSERT 0 1
    11:55:03 (xdb@[local]:5432)testdb=# 
    11:55:38 (xdb@[local]:5432)testdb=# begin;
    BEGIN
    11:55:38 (xdb@[local]:5432)testdb=#* 
    11:55:38 (xdb@[local]:5432)testdb=#* delete from t_mvcc2 where c1 = 1;
    DELETE 1
    11:55:38 (xdb@[local]:5432)testdb=#* 
    11:55:38 (xdb@[local]:5432)testdb=#* commit;
    COMMIT
    11:55:38 (xdb@[local]:5432)testdb=# 
    11:55:38 (xdb@[local]:5432)testdb=# begin;
    BEGIN
    11:55:38 (xdb@[local]:5432)testdb=#* 
    11:55:38 (xdb@[local]:5432)testdb=#* update t_mvcc2 set c2 = 'C2#'||substr(c2,4,40) where c1 = 2;
    UPDATE 1
    11:55:38 (xdb@[local]:5432)testdb=#* 
    12:19:17 (xdb@[local]:5432)testdb=#* select txid_current();
     txid_current 
    --------------
             2363
    (1 row)
    
    

    另外启动session 2,查询数据表t_mvcc2

    11:58:45 (xdb@[local]:5432)testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc2',0));
     lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask 
    ----+--------+----------+--------+--------+-------+--------+-------------+------------
      1 |   8152 |        1 |   2360 |   2362 |     0 | (0,1)  |        8195 |       1282
      2 |   8112 |        1 |   2361 |   2363 |     0 | (0,3)  |       16387 |        258
      3 |   8072 |        1 |   2363 |      0 |     0 | (0,3)  |       32771 |      10242
    (3 rows)
    11:59:38 (xdb@[local]:5432)testdb=# select * from t_mvcc2;
    
    

    启动gdb,设置断点

    (gdb) b HeapTupleSatisfiesMVCC
    Breakpoint 1 at 0xa93125: file tqual.c, line 966.
    (gdb) c
    Continuing.
    
    Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=69) at tqual.c:966
    966     HeapTupleHeader tuple = htup->t_data;
    (gdb) 
    

    查看调用栈

    (gdb) bt
    #0  HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=69) at tqual.c:966
    #1  0x00000000004de959 in heap_hot_search_buffer (tid=0x1985d1c, relation=0x7f002f73e2b0, buffer=69, snapshot=0x1a06358, 
        heapTuple=0x1985d18, all_dead=0x7ffc56ed8862, first_call=true) at heapam.c:2127
    #2  0x00000000004fd12e in index_fetch_heap (scan=0x1985cb8) at indexam.c:608
    #3  0x00000000004fd35d in index_getnext (scan=0x1985cb8, direction=ForwardScanDirection) at indexam.c:691
    #4  0x00000000004fb910 in systable_getnext (sysscan=0x19868f8) at genam.c:425
    #5  0x0000000000a1cb63 in SearchCatCacheMiss (cache=0x19de480, nkeys=1, hashValue=1281076841, hashIndex=105, v1=42610, 
        v2=0, v3=0, v4=0) at catcache.c:1386
    #6  0x0000000000a1ca10 in SearchCatCacheInternal (cache=0x19de480, nkeys=1, v1=42610, v2=0, v3=0, v4=0) at catcache.c:1317
    #7  0x0000000000a1c6fe in SearchCatCache1 (cache=0x19de480, v1=42610) at catcache.c:1185
    #8  0x0000000000a37543 in SearchSysCache1 (cacheId=50, key1=42610) at syscache.c:1119
    #9  0x0000000000a6ae60 in check_enable_rls (relid=42610, checkAsUser=0, noError=false) at rls.c:66
    #10 0x000000000087b286 in get_row_security_policies (root=0x1985dd0, rte=0x1985ee8, rt_index=1, 
        securityQuals=0x7ffc56ed8cc0, withCheckOptions=0x7ffc56ed8cb8, hasRowSecurity=0x7ffc56ed8cb7, 
        hasSubLinks=0x7ffc56ed8cb6) at rowsecurity.c:133
    #11 0x0000000000875e1c in fireRIRrules (parsetree=0x1985dd0, activeRIRs=0x0) at rewriteHandler.c:1904
    #12 0x0000000000878b23 in QueryRewrite (parsetree=0x1985dd0) at rewriteHandler.c:3712
    #13 0x00000000008c64d7 in pg_rewrite_query (query=0x1985dd0) at postgres.c:782
    #14 0x00000000008c634e in pg_analyze_and_rewrite (parsetree=0x1985c20, query_string=0x1984ec8 "select * from t_mvcc2;", 
        paramTypes=0x0, numParams=0, queryEnv=0x0) at postgres.c:698
    #15 0x00000000008c6993 in exec_simple_query (query_string=0x1984ec8 "select * from t_mvcc2;") at postgres.c:1070
    #16 0x00000000008cae70 in PostgresMain (argc=1, argv=0x19b0dc8, dbname=0x19b0c30 "testdb", username=0x1981ba8 "xdb")
        at postgres.c:4182
    #17 0x000000000082642b in BackendRun (port=0x19a6c00) at postmaster.c:4361
    #18 0x0000000000825b8f in BackendStartup (port=0x19a6c00) at postmaster.c:4033
    #19 0x0000000000821f1c in ServerLoop () at postmaster.c:1706
    #20 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x197fb60) at postmaster.c:1379
    ---Type <return> to continue, or q <return> to quit---
    #21 0x00000000007488ef in main (argc=1, argv=0x197fb60) at main.c:228
    (gdb) 
    

    第一个Tuple
    输入参数,主要是tuple/snapshot/buffer

    (gdb) p *snapshot --> 快照信息
    $3 = {satisfies = 0xa9310d <HeapTupleSatisfiesMVCC>, xmin = 2363, xmax = 2363, xip = 0x0, xcnt = 0, subxip = 0x0, 
      subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = true, curcid = 0, speculativeToken = 0, 
      active_count = 0, regd_count = 1, ph_node = {first_child = 0x0, next_sibling = 0x0, 
        prev_or_parent = 0xf9bfa0 <CatalogSnapshotData+64>}, whenTaken = 0, lsn = 0}
    

    获取tuple
    t_infomask = 2313,十六进制为0x0909,即HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL

    (gdb) n
    968     Assert(ItemPointerIsValid(&htup->t_self));
    (gdb) p tuple
    $1 = (HeapTupleHeader) 0x7f0002da7a60
    (gdb) p *tuple
    $2 = {t_choice = {t_heap = {t_xmin = 2359, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 2359, 
          datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 12}, ip_posid = 6}, t_infomask2 = 33, 
      t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002da7a77 "\377\377\377?"}
    (gdb) 
    

    判断tuple.xmin是否已提交,按上一步的t_infomask标记,该事务已提交,进入相应逻辑

    (gdb) n
    969     Assert(htup->t_tableOid != InvalidOid);
    (gdb) 
    971     if (!HeapTupleHeaderXminCommitted(tuple))
    (gdb) 
    

    判断:B.xmin事务已提交,但可能不属于该快照
    不符合此条件,继续执行

    (gdb) n
    1074                XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
    (gdb) 
    1073            if (!HeapTupleHeaderXminFrozen(tuple) &&
    (gdb) 
    1080        if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid or aborted */
    (gdb) 
    

    判断是否HEAP_XMAX_INVALID,按t_infomask标记,符合条件,返回T

    (gdb) 
    1080        if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid or aborted */
    (gdb) n
    1081            return true;
    (gdb) 
    1148    }
    (gdb) 
    

    第二个Tuple
    接下来是第二个Tuple

    (gdb) c
    Continuing.
    
    Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=155) at tqual.c:966
    966     HeapTupleHeader tuple = htup->t_data;
    (gdb) 
    

    获取tuple并查看
    t_infomask = 2313,十六进制为0x0909,即HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL

    (gdb) p *htup
    $4 = {t_len = 172, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 40}, t_tableOid = 1247, t_data = 0x7f0002e52800}
    (gdb) n
    968     Assert(ItemPointerIsValid(&htup->t_self));
    (gdb) p tuple
    $5 = (HeapTupleHeader) 0x7f0002e52800
    (gdb) p *tuple
    $6 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1, 
          datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 40}, t_infomask2 = 30, 
      t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002e52817 "\377\377\377\a"}
    (gdb) 
    

    与第一个Tuple类似,下面查看第三个Tuple
    第三个Tuple
    获取tuple并查看
    t_infomask = 10505,十六进制值为0x2909,即HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL

    (gdb) c
    Continuing.
    
    Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0xf9bf60 <CatalogSnapshotData>, buffer=4) at tqual.c:966
    966     HeapTupleHeader tuple = htup->t_data;
    (gdb) n
    968     Assert(ItemPointerIsValid(&htup->t_self));
    (gdb) 
    969     Assert(htup->t_tableOid != InvalidOid);
    (gdb) p *tuple
    $7 = {t_choice = {t_heap = {t_xmin = 2117, t_xmax = 0, t_field3 = {t_cid = 7, t_xvac = 7}}, t_datum = {datum_len_ = 2117, 
          datum_typmod = 0, datum_typeid = 7}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 58}, t_infomask2 = 33, 
      t_infomask = 10505, t_hoff = 32 ' ', t_bits = 0x7f0002d25aa7 "\377\377\377?"}
    (gdb) 
    

    第三个Tuple,类似于第一/二个Tuple,仍返回T

    (gdb) n
    971     if (!HeapTupleHeaderXminCommitted(tuple))
    (gdb) 
    1073            if (!HeapTupleHeaderXminFrozen(tuple) &&
    (gdb) 
    1074                XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
    (gdb) 
    1073            if (!HeapTupleHeaderXminFrozen(tuple) &&
    (gdb) 
    1080        if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid or aborted */
    (gdb) 
    1081            return true;
    (gdb) 
    1148    }
    

    第四/五/六个Tuple
    均有HEAP_XMAX_INVALID标志,不作展开

    (gdb) c
    Continuing.
    
    Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=44) at tqual.c:966
    966     HeapTupleHeader tuple = htup->t_data;
    (gdb) n
    968     Assert(ItemPointerIsValid(&htup->t_self));
    (gdb) 
    969     Assert(htup->t_tableOid != InvalidOid);
    (gdb) p *tuple
    $8 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 75, t_xvac = 75}}, t_datum = {datum_len_ = 1, 
          datum_typmod = 0, datum_typeid = 75}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 21}, ip_posid = 1}, t_infomask2 = 24, 
      t_infomask = 2305, t_hoff = 32 ' ', t_bits = 0x7f0002d76307 "\377\377\017"}
    (gdb) 
    ...
    (gdb) p *tuple
    $9 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 75, t_xvac = 75}}, t_datum = {datum_len_ = 1, 
          datum_typmod = 0, datum_typeid = 75}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 19}, t_infomask2 = 20, 
      t_infomask = 2307, t_hoff = 32 ' ', t_bits = 0x7f0002d7368f "\377\377\003"}
    ...
    (gdb) p *tuple
    $10 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1, 
          datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 1}, t_infomask2 = 4, 
      t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002ee632f "\003"}
    

    第七个Tuple
    t_infomask = 1282,0x0502,即HEAP_XMAX_COMMITTED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH

    (gdb) c
    Continuing.
    
    Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x7ffc56ed8920, snapshot=0x1a062c0, buffer=206) at tqual.c:966
    966     HeapTupleHeader tuple = htup->t_data;
    (gdb) n
    968     Assert(ItemPointerIsValid(&htup->t_self));
    (gdb) 
    969     Assert(htup->t_tableOid != InvalidOid);
    (gdb) p *tuple
    $11 = {t_choice = {t_heap = {t_xmin = 2360, t_xmax = 2362, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {
          datum_len_ = 2360, datum_typmod = 2362, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, 
        ip_posid = 1}, t_infomask2 = 8195, t_infomask = 1282, t_hoff = 24 '\030', t_bits = 0x7f0002eba36f ""}
    (gdb) 
    

    这是被"deleted"的tuple,2362事务已提交,不可见

    1086        if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
    (gdb) 
    1113        if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
    (gdb) 
    1141            if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
    (gdb) n
    1147        return false;
    (gdb) 
    

    第八个Tuple
    t_infomask = 258,0x0102,即HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH

    (gdb) p *tuple
    $12 = {t_choice = {t_heap = {t_xmin = 2361, t_xmax = 2363, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {
          datum_len_ = 2361, datum_typmod = 2363, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, 
        ip_posid = 3}, t_infomask2 = 16387, t_infomask = 258, t_hoff = 24 '\030', t_bits = 0x7f0002eba347 ""}
    (gdb) 
    

    这是正在update的tuple,应可见

    1080        if (tuple->t_infomask & HEAP_XMAX_INVALID)  /* xid invalid or aborted */
    (gdb) 
    1083        if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
    (gdb) 
    1086        if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
    (gdb) 
    1113        if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
    (gdb) 
    1115            if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))  --> 非当前事务
    (gdb) 
    1123            if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot)) --> 事务快照标志该事务进行中
    (gdb) 
    1124                return true; --> 可见
    (gdb) 
    

    第九个Tuple
    t_infomask = 10242,0x2802,即HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH

    (gdb) p *tuple
    $13 = {t_choice = {t_heap = {t_xmin = 2363, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 2363, 
          datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 3}, 
      t_infomask2 = 32771, t_infomask = 10242, t_hoff = 24 '\030', t_bits = 0x7f0002eba31f ""}
    (gdb) 
    

    这是update操作新生成的tuple,不可见

    (gdb) n
    971     if (!HeapTupleHeaderXminCommitted(tuple)) --> xmin未提交
    (gdb) 
    973         if (HeapTupleHeaderXminInvalid(tuple))
    (gdb) 
    977         if (tuple->t_infomask & HEAP_MOVED_OFF)
    (gdb) 
    996         else if (tuple->t_infomask & HEAP_MOVED_IN)
    (gdb) 
    1015            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple))) --> 非当前事务
    (gdb) 
    1057            else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot)) --> xmin事务处于活动中
    (gdb) 
    1058                return false; --> 不可见
    (gdb) 
    

    查询结果

    11:59:38 (xdb@[local]:5432)testdb=# select * from t_mvcc2;
     c1 |  c2  |  c3  
    ----+------+------
      2 | C2-2 | C3-2
    (1 row)
    

    DONE!

    四、参考资料

    PG Source Code

    相关文章

      网友评论

        本文标题:PostgreSQL 源码解读(118)- MVCC#3(Tup

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