postgres源码解析32 快照可见性判断HeapTupleSatisfiesMVCC

简介

  PostgreSQL利用快照和多版本实现了MVCC技术和快照隔离。利用快照判断元组的可见性在是事务操作中最为关键的一个环节,接口函数为HeapTupleSatisfiesMVCC。
快照相关概念知识: Postgres 源码解析4 MVCC 快照 (Snapshot)获取
元组可见性判断:postgres 源码解析2 元组可见性判断 t_infomask标识位

源码解析
1 接口定义
/*
 * HeapTupleSatisfiesMVCC
 *		True iff heap tuple is valid for the given MVCC snapshot.
 *
 * See SNAPSHOT_MVCC's definition for the intended behaviour.
 *
 * 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 ProcArrayLock.
 */
 // 在快照范围内的事务不会更新其插入/删除状态元组的标识位。标志位的更新时机为事务完成后元组的第
 // 一次访问。
 static bool
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
					   Buffer buffer)
2 流程分析

2.1 xmin未提交,元组在生成阶段

static bool
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
					   Buffer buffer)
{
	HeapTupleHeader tuple = htup->t_data;

	Assert(ItemPointerIsValid(&htup->t_self));
	Assert(htup->t_tableOid != InvalidOid);
// xmin未提交 
	if (!HeapTupleHeaderXminCommitted(tuple))
	{
		// xmin无效,元组正在插入
		if (HeapTupleHeaderXminInvalid(tuple))
			return false;

		/* Used by pre-9.0 binary upgrades */
		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)))
		{
			// 插入元组的动作 在获取快照之后   ==》 元组插入 cid 》= snapshotcurcid
			// 不可见 
			if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
				return false;	/* inserted after scan started */
			
			// xmax ,元组被删除或更新,但是操作被回滚,则元组对于本事务可见
			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
				return true;
			
			// 元组只是被锁住 ,可见
			if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))	/* not deleter */
				return true;
			
			// multi事务 
			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
			{
				// 元组在事务内生成,但被其他事务修改 ===》 这种操作只能发生在该事务的子事务中
				TransactionId xmax;
				// 获取xmax 
				xmax = HeapTupleGetUpdateXid(tuple);

				/* not LOCKED_ONLY, so it has to have an xmax */
				Assert(TransactionIdIsValid(xmax));

				/* updating subtransaction must have aborted */
				// 更新子事务终止,则元组可见
				if (!TransactionIdIsCurrentTransactionId(xmax))
					return true;
				// 在快照获取后更新,可见,反之不可见
				else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
					return true;	/* updated after scan started */
				else
					return false;	/* updated before scan started */
			}

			// 子事务操作回滚,元组可见
			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
			{
				/* deleting subtransaction must have aborted */
				//子事务回滚, 设置标识位
				SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
							InvalidTransactionId);
				return true;
			}
			
			// 在获取快照前执行删除,元组可见,反之不可见 
			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
				return true;	/* deleted after scan started */
			else
				return false;	/* deleted before scan started */
		}
		// xmin处于活跃事务链表中,不可见
		else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
			return false;
		// 元组提交,则添加HEAP_XMIN_COMMITTED标识位
		else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
			SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
						HeapTupleHeaderGetRawXmin(tuple));
		// 回滚,则添加 HEAP_XMIN_INVALID标识位
		else
		{
			/* it must have aborted or crashed */
			SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
						InvalidTransactionId);
			return false;
		}
	}

2.2 xmin提交,但xmax未提交

else
	{
		/* xmin is committed, but maybe not according to our snapshot */
		// xmin 提交,但处于活跃快照列表中,则不可见 ===》等价视为运行中
		if (!HeapTupleHeaderXminFrozen(tuple) &&
			XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
			return false;		/* treat as still in progress */
	}

	/* by here, the inserting transaction has committed */
	// xmin提交,xmax无效,说明插入成功或者删除回滚,元组可见
	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
		return true;
    
    // 元组被锁住,可见
	if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
		return true;
    
    // xmax为 multi事务,说明元组被其他事务修改 
	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
	{
		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))
		{
			// 修改动作迟于快照获取时机,则元组可见,反之不可见
			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
				return true;	/* deleted after scan started */
			else
				return false;	/* deleted before scan started */
		}
		// 修改操作事务处于活跃事务列表,元组可见
		if (XidInMVCCSnapshot(xmax, snapshot))
			return true;
		/ 修改操作事务已经提交,则元组不可见,反之可见
		if (TransactionIdDidCommit(xmax))
			return false;		/* updating transaction committed */
		/* it must have aborted or crashed */
		return true;
	}

2.3 xmin提交,xmax也提交

// xmin提交,但元组标识位还未更新
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
	{
		// 删除操作为当前事务
		if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
		{
			// 删除动作发生在获取快照之后,元组可见 ==》 对快照来说,删除还未发生
			// 反之可见
			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
				return true;	/* deleted after scan started */
			else
				return false;	/* deleted before scan started */
		}
	
	    // 删除操作在活跃事务列表中,元组可见 ===》 正在删除,删除动作未完成
		if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
			return true;
		
		// xmax 为commit, 删除操作回滚, 更新 HEAP_XMAX_INVALID标识位
		// ===》 通过clog 获取事务提交状态
		if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
		{
			/* it must have aborted or crashed */
			SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
						InvalidTransactionId);
			return true;
		}
		
		// 
		/* xmax transaction committed */
		SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
					HeapTupleHeaderGetRawXmax(tuple));
	}
	
	// xamx 提交,且在快照中,则元组可见  ===》等价于更新操作未完成
	else
	{
		/* xmax is committed, but maybe not according to our snapshot */
		if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
			return true;		/* treat as still in progress */
	}

	/* xmax transaction committed */
	// 提交,不可见
	return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值