事务机制源码解析| 事务并发控制(3)
5.2.3 MVCC可见性判断机制
openGauss利用多版本并发控制来维护数据的一致性。当扫描数据时每个事务看到的只是拿快照那一刻的数据,而不是数据当前的最新状态。这样就可以避免一个事务看到其他并发事务的更新而导致不一致的场景。使用多版本并发控制的主要优点是读取数据的锁请求与写数据的锁请求不冲突,以此来实现读不阻塞写,写也不阻塞读。下面介绍事务的隔离级别以及openGauss可见性判断CSN机制。
1. 事务隔离级别
SQL标准考虑了并行事务间应避免的现象,定义了以下几种隔离级别,如表5-5所示。

(1) 脏写(dirty write):两个事务分别写入,两个事务分别提交或回滚,则事务的结果无法确定,即一个事务可以回滚另一个事务的提交。
(2) 脏读(dirty read):一个事务可以读取另一个事务未提交的修改数据。
(3) 不可重复读(fuzzy read):一个事务重复读取前面读取过的数据,数据的结果被另外的事务修改。
(4) 幻读(phantom):一个事务重复执行范围查询,返回一组符合条件的数据,每次查询的结果集因为其他事务的修改发生改变(条数)。
在各类数据库实现的过程中,并发事务产生了一些新的现象,在原来的隔离级别的基础上,有了一些扩展。如表5-6所示。

(5) 更新丢失(lost update):一个事务在读取元组并更新该元组的过程中,有另一个事务修改了该元组的值,导致最终这次修改丢失。
(6) 读偏斜(read skew):假设数据x,y有隐式的约束x+y=100;事务一读取x=50;事务二写x=25并更新y=75保证约束成立,事务二提交,事务一再读取y=75,导致事务一中读取x+y=125,不满足约束。
(7) 写偏斜(write skew):假设数据x,y有隐式的约束x+y= snapshot->curcid)
                return true; /* 在扫面前该删除事务已经提交 */
            else
                return false; /* 扫描开始后删除操作的事务才提交 */
        }
        visible = XidVisibleInSnapshot(HeapTupleHeaderGetXmax(page, tuple), snapshot, &hintstatus);
        if (hintstatus == XID_COMMITTED) {
            /* 设置xmax的hint bit */
            SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetXmax(page, tuple));
        }
        if (hintstatus == XID_ABORTED) {
            /* 回滚或者故障 */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
        }
        if (!visible) {
            return true; /* 快照中xmax对应的事务不可见,则认为该元组仍然活跃 */
        }
    } else {
        /* xmax对应的事务已经提交,但是快照中该事务不可见,认为删除该元组的操作未完成,仍然认为该元组可见 */
        if (!CommittedXidVisibleInSnapshot(HeapTupleHeaderGetXmax(page, tuple), snapshot)) {
            return true; /* 认为元组可见 */
        }
    }
    return false;
}
3) HeapTupleSatisfiesNow
该函数的逻辑同MVCC类似,只是此时并没有统一快照,而仅仅是判断当前xmin/xmax的状态,而不再继续调用XidVisibleInSnapshot函数、CommittedXidVisibleInSnapshot函数来判断是否对快照可见。
4) HeapTupleSatisfiesVacuum
根据传入的OldestXmin的值返回相应的状态。死亡元组(openGauss多版本机制中不可见的旧版本元组)且没有任何其他未结束的事务可能访问该元组(xmaxt_infomask & HEAP_XMAX_INVALID)  /* 如果xmax还没有,说明没有人删除,此时判断该元组正在插入过程中,否则在删除过程中  */
                return HEAPTUPLE_INSERT_IN_PROGRESS;
            return HEAPTUPLE_DELETE_IN_PROGRESS;  /* 返回正在删除的过程中  */
        } else if (xidstatus == XID_COMMITTED) {  /* 如果xmin提交了,打上hint bit,后面继续看xmax是否提交。  */
            SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleGetRawXmin(htup));
        } else {
   ….  /* 事务结束了且未提交,可能是abort或者是crash的事务,一般返回死亡,可删除;单机情形 t_thrd.xact_cxt.useLocalSnapshot没有作用,恒为false。 */
            SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId);
            return ((!t_thrd.xact_cxt.useLocalSnapshot || IsInitdb) ? HEAPTUPLE_DEAD : HEAPTUPLE_LIVE);
        }
    }
  /* 接着判断xmax。如果还没有设置xmax说明没有人删除该元组,返回元组存活,不可删除。  */
    if (tuple->t_infomask & HEAP_XMAX_INVALID)
        return HEAPTUPLE_LIVE;
……
    if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) {   /*如果xmax提交,则看xmax是否比oldesxmin小。小的话说明没有未结束的事务会访问该元组,可以删除。 */
        xidstatus = TransactionIdGetStatus(HeapTupleGetRawXmax(htup), false);
        if (xidstatus == XID_INPROGRESS)
            return HEAPTUPLE_DELETE_IN_PROGRESS;
         else if (xidstatus == XID_COMMITTED)
            SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleGetRawXmax(htup));
        else {
…  /*  xmax对应的事务abort或者crash */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
            return HEAPTUPLE_LIVE;
        }
    }
/*判断该元组是否可以删除,xmax
 
 
                     
                     
                     
                    