海山数据库(He3DB)源码详解:MVCC可见性HeapTupleSatisfiesSelf函数

21 阅读5分钟

海山数据库(He3DB)源码详解:MVCC可见性HeapTupleSatisfiesSelf函数

本文介绍了事务执行过程中,检查一个堆元组是否对当前事务可见,用于确保事务可以看到正确的数据版本。

HeapTupleSatisfiesSelf函数源码解读

HeapTupleSatisfiesSelf:用于判断一个元组是否对当前事务可见;

函数声明
static bool
HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)

参数说明:

  • HeapTuple htup:指向堆元组的指针;
  • Snapshot snapshot:元组对应的快照;
  • Buffer buffer:包含堆元组的缓冲区。
函数说明:

该函数用于判断一个元组(tuple)是否对其自身有效,决定了当前事务能否看到某个特定的元组版本。该函数的流程图如下:

在这里插入图片描述

HeapTupleSatisfiesSelf函数过程
  • 获取元组头,并确保元组的自指针和元组的表OID有效
	HeapTupleHeader tuple = htup->t_data;

	Assert(ItemPointerIsValid(&htup->t_self));
	Assert(htup->t_tableOid != InvalidOid);
首先,检查Xmin事务是否已经提交,如果没有提交,执行以下过程;
	if (!HeapTupleHeaderXminCommitted(tuple))
	{
		/* 1.检查元组的Xmin有效性 */
		/* 2.检查元组是否被标记为HEAP_MOVED_OFF */
		/* 3.检查元组是否被标记为HEAP_MOVED_IN  */
		/* 4.检查Xmin事务ID是否为当前事务  */
		/* 5.检查Xmin事务ID是否为在运行中  */
		/* 6.检查Xmin事务ID是否为已经提交  */
		/* 7.其他(事务中止或系统崩溃) */
	}
  1. 如果Xmin事务已经提交或者中止,返回false;
	if (HeapTupleHeaderXminInvalid(tuple))
		return false;
  1. 获取事务IDxvac,并判断是否为当前事务ID,以及是否在进行中:
  • 事务IDxvac为当前事务ID,返回false;
  • 事务IDxvac不在进行中且已经提交,则设置Xmin的提示位为XMIN已无效,返回false;
  • 事务IDxvac不在进行中且没有提交,则设置Xmin的提示位为XMIN已提交(后续执行提交行为)。
	/* Used by pre-9.0 binary upgrades */
	if (tuple->t_infomask & HEAP_MOVED_OFF)
	{
		TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

		if (TransactionIdIsCurrentTransactionId(xvac))
			return false;
		if (!TransactionIdIsInProgress(xvac))
		{
			if (TransactionIdDidCommit(xvac))
			{
				SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
							InvalidTransactionId);
				return false;
			}
			SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
						InvalidTransactionId);
		}
	}
  1. 获取事务IDxvac,并判断是否为当前事务ID:
  • 如果事务IDxvac不是当前事务,且在后台运行中,返回false;
  • 如果事务IDxvac不是当前事务,且已经提交,则设置Xmin的提示位为XMIN已提交;
  • 如果事务IDxvac不是当前事务,且没有提交,则设置Xmin的提示位为XMIN已无效,返回false。
	else if (tuple->t_infomask & HEAP_MOVED_IN)
	{
		TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

		if (!TransactionIdIsCurrentTransactionId(xvac))
		{
			if (TransactionIdIsInProgress(xvac))
				return false;
			if (TransactionIdDidCommit(xvac))
				SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
							InvalidTransactionId);
			else
			{
				SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
							InvalidTransactionId);
				return false;
			}
		}
	}
  1. 依次检查元组Xmax的提示位、锁的状态和事务ID状态:
  • 如果元组的Xmax无效,返回true;
  • 如果元组的Xmax仅是锁定位,返回true;
  • 如果元组的Xmax是组合事务,则获取Xmax的事务ID,并判断是否为当前事务ID,是:返回true,不是:返回false;
  • 如果元组的Xmax的事务ID不是当前事务,设置提示位为XMAX已无效,并返回true;
  • 其他,返回false。
	else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
	{
		if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
			return true;

		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))	/* not deleter */
			return true;

		if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
		{
			TransactionId 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
				return false;
		}

		if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
		{
			/* deleting subtransaction must have aborted */
			SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
						InvalidTransactionId);
			return true;
		}

		return false;
	}
  1. 如果Xmin的事务还在运行中,返回false;
  2. 如果Xmin的事务已经提交,则设置元组提示位为XMIN已提交。
	else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
		return false;
	else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
		SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
					HeapTupleHeaderGetRawXmin(tuple));
  1. 其他清空,则设置元组提示位为XMIN已无效,并返回false。
	else
	{
		/* it must have aborted or crashed */
		SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
					InvalidTransactionId);
		return false;
	}
其次,如果Xmin事务已经提交,则判断Xmax的状态:
  1. 如果Xmax无效,返回true;
	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
		return true;
  1. 如果Xmax已提交,且有锁定位,返回true;否则返回false;
	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
	{
		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
			return true;
		return false;			/* updated by other */
	}
  1. 如果Xmax是组合事务,获取Xmax事务的ID,并判断:
  • 如果Xmax仅是锁定位,返回true;
  • 如果是当前事务,返回false;
  • 如果是在运行中,返回true;
  • 如果是已提交,返回false;
  • 其他返回true(中止或者崩溃);
	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
	{
		TransactionId xmax;

		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
			return true;

		xmax = HeapTupleGetUpdateXid(tuple);

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

		if (TransactionIdIsCurrentTransactionId(xmax))
			return false;
		if (TransactionIdIsInProgress(xmax))
			return true;
		if (TransactionIdDidCommit(xmax))
			return false;
		/* it must have aborted or crashed */
		return true;
	}
  1. 如果Xmax是当前事务,且有锁定位,返回true;无锁定位,返回false;
  2. 如果Xmax的事务在运行中,返回true;
  3. 如果Xmax的事务已提交,设置元组的提示位为XMAX_INVALID,并返回true;
  4. 如果Xmax的事务仅有锁定位,设置元组的提示位为XMAX_INVALID,并返回true;
  5. 其余情况将元组的提示位设置为XMAX_COMMITTED,并返回false。
	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
	{
		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
			return true;
		return false;
	}

	if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
		return true;

	if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
	{
		/* it must have aborted or crashed */
		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
					InvalidTransactionId);
		return true;
	}

	/* xmax transaction committed */

	if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
	{
		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
					InvalidTransactionId);
		return true;
	}
	SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
				HeapTupleHeaderGetRawXmax(tuple));
	return false;

该函数通过检查XminXmax事务的状态(是否已提交、是否进行中、是否是当前事务等),来确定元组对自身是否有效。并且通过设置元组的提示位以有优化未来的可见性检查。

	InvalidTransactionId);
	return true;
}
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
			HeapTupleHeaderGetRawXmax(tuple));
return false;

该函数通过检查**Xmin****Xmax**事务的状态(是否已提交、是否进行中、是否是当前事务等),来确定元组对自身是否有效。并且通过设置元组的提示位以有优化未来的可见性检查。