postgresql源码学习(四)—— 启动事务

67 篇文章 52 订阅
34 篇文章 3 订阅

一、 虚拟事务id

       前篇我们说到,执行dml操作时,才会为事务分配事务id。不过,即使没有事务id,事务也会用一个虚拟事务id来代表自己。

       虚拟事务id由两部分组成:backendId(后台进程id,会话独有)+ localTransactionId(进程维护的本地事务id),以下结构体代码在 lock.h

typedef struct
{
	BackendId	backendId;		/* backendId from PGPROC */
	LocalTransactionId localTransactionId;	/* lxid from PGPROC */
} VirtualTransactionId;

        在事务启动阶段,由于不知道事务是否会有dml操作,此时只会先分配虚拟事务id。

二、 StartTransaction()函数

1. 函数调用栈

2. 源码与跟踪过程

static void
StartTransaction(void)
{
	TransactionState s;        //事务栈结构体
	VirtualTransactionId vxid; //虚拟事务id

	/*
	 * 确保事务栈是空的
	 */
	s = &TopTransactionStateData;
	CurrentTransactionState = s; //当前事务

	Assert(!FullTransactionIdIsValid(XactTopFullTransactionId)); //未分配事务id?

	/* check the current transaction state */
	Assert(s->state == TRANS_DEFAULT);

	/*
	 * Set the current transaction state information appropriately during
	 * start processing.  Note that once the transaction status is switched
	 * this process cannot fail until the user ID and the security context
	 * flags are fetched below.
* 在启动过程中设置当前事务状态信息。请注意,一旦切换了事务状态,在后续获取用户ID和安全上下文标志前,不会出现异常。
	 */
	s->state = TRANS_START; //修改事务状态
	s->fullTransactionId = InvalidFullTransactionId;	/* until assigned,因为事务id尚未分配,目前是invalid的,InvalidFullTransactionId其实就是0 */

	/* Determine if statements are logged in this transaction */
	xact_is_sampled = log_xact_sample_rate != 0 &&
		(log_xact_sample_rate == 1 ||
		 random() <= log_xact_sample_rate * MAX_RANDOM_VALUE);

/*
	 * initialize current transaction state fields
	 * note: prevXactReadOnly is not used at the outermost level
	 * 初始化事务结构体字段,注意:prevXactReadOnly不会在最外层中使用
	 */
	s->nestingLevel = 1;
	s->gucNestLevel = 1;
	s->childXids = NULL;
	s->nChildXids = 0;
	s->maxChildXids = 0;

 ​​​​​

/*
	 * Once the current user ID and the security context flags are fetched,
	 * both will be properly reset even if transaction startup fails.
* 一旦当前用户ID和安全上下文标记已提取,即使事务启动失败,也会正确地重置它们。
	 */
	GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);

	/* SecurityRestrictionContext should never be set outside a transaction,SecurityRestrictionContext不应在事务外设置 */
	Assert(s->prevSecContext == 0);

/*
	 * Make sure we've reset xact state variables
	 *
	 * If recovery is still in progress, mark this transaction as read-only.
	 * We have lower level defences in XLogInsert and elsewhere to stop us
	 * from modifying data during recovery, but this gives the normal
	 * indication to the user that the transaction is read-only.
* 如仍处于恢复过程,标志此事务为只读
* 在XLogInsert中和其他地方有低级别的保护机制确保在恢复过程中不会更新数据,只是给用户正常的提示,说明事务只读
	 */
	if (RecoveryInProgress())
	{
		s->startedInRecovery = true;
		XactReadOnly = true;
	}
	else
	{
		s->startedInRecovery = false;
		XactReadOnly = DefaultXactReadOnly;
	}

	XactDeferrable = DefaultXactDeferrable;
	XactIsoLevel = DefaultXactIsoLevel;
	forceSyncCommit = false;
	MyXactFlags = 0;

	/*
	 * reinitialize within-transaction counters,重新初始化事务内计数器
	 */
	s->subTransactionId = TopSubTransactionId;
	currentSubTransactionId = TopSubTransactionId;
	currentCommandId = FirstCommandId;
	currentCommandIdUsed = false;

/*
	 * initialize reported xid accounting,初始化已报告的事务计数
	 */
	nUnreportedXids = 0;
	s->didLogXid = false;

 

/*
	 * must initialize resource-management stuff first,首先初始化资源管理器
	 */
	AtStart_Memory(); //初始化两个内存上下文,一个用于abort事务、一个用于top事务
	AtStart_ResourceOwner();//初始化资源跟踪器,其中包含buffer信息、锁信息等

	/*
	 * 前面提到的,虚拟事务id的两个组成部分
	 */
	vxid.backendId = MyBackendId;
	vxid.localTransactionId = GetNextLocalTransactionId();

	/*
	 * Lock the virtual transaction id before we announce it in the proc array。锁住该虚拟事务id
	 */
	VirtualXactLockTableInsert(vxid);

	/*
	 * Advertise it in the proc array.  We assume assignment of
	 * localTransactionId is atomic, and the backendId should be set already.
* 在proc array中声明。假定LocalTransactionID是原子的且backendId已分配。将本地事务id保存至当前进程队列(PROC)
	 */
	Assert(MyProc->backendId == vxid.backendId);
	MyProc->lxid = vxid.localTransactionId;

TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);

	/*
	 * 设置时间戳。设置事务开始时间=命令开始时间,并初始化事务结束时间=0
	 */
	if (!IsParallelWorker())
	{
		if (!SPI_inside_nonatomic_context())
			xactStartTimestamp = stmtStartTimestamp;
		else
			xactStartTimestamp = GetCurrentTimestamp();
	}
	else
		Assert(xactStartTimestamp != 0);
	pgstat_report_xact_timestamp(xactStartTimestamp);
	/* Mark xactStopTimestamp as unset. */
	xactStopTimestamp = 0;

	/*
	 * initialize other subsystems for new transaction
	 */
	AtStart_GUC(); //多个子事务可能设置不同的GUC参数,需要记录修改历史,在rollback子事务时进行恢复
	AtStart_Cache(); //失效消息机制
	AfterTriggerBeginXact();// after-trigger相关

 补充:GUC参数案例如下

/*
	 * done with start processing, set current transaction state to "in
	 * progress"。启动事务完成,将事务状态改为TRANS_INPROGRESS
	 */
	s->state = TRANS_INPROGRESS;

	ShowTransactionState("StartTransaction");
}

至此执行完毕,输入c结束,begin命令运行完成

3. 执行流程概要

 

参考

PostgreSQL技术内幕:事务处理深度探索》第1章

《PostgreSQL数据库内核分析》第7章

http://blog.itpub.net/6906/viewspace-2564019/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hehuyi_In

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值