pg事务:事务的处理

文章详细介绍了PostgreSQL中事务的处理机制,包括隐式和显示事务的区别,事务块的开始和结束命令,以及事务处理的三个层次函数。事务必须满足原子性,执行过程中出错会回滚。文章还讨论了事务状态和事务块状态的枚举类型及其流转过程,并提及了事务处理的相关函数和命令调用顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务的处理

事务块

从事务形态划分可分为隐式事务和显示事务。隐式事务是一个独立的SQL语句,执行完成后默认提交。显示事务需要显示声明一个事务,多个sql语句组合到一起称为一个事务块。

事务块通过beginbegin transactionstart transaction开始

通过COMMIT,ENDABORT,ROLLBACK结束,其中COMMIT=ENDABORT=ROLLBACK

BEGIN;
select * from lzl1 limit 1;
update lzl1 set a=2;
END;

如果事务块执行过程中一旦有报错,由于事务必须满足原子性,事务只能回退

lzldb=# begin;
BEGIN
lzldb=*# select * from lzl2;
ERROR: relation "lzl2" does not exist
LINE 1: select * from lzl2;
^
lzldb=!# commit;
ROLLBACK

事务处理函数

事务处理函数分为3个层次:顶层事务函数、中层事务函数、底层事务函数
顶层事务函数,处理事务块命令,比如BEGIN, COMMIT, ROLLBACK, SAVEPOINT等,有如下函数

BeginTransactionBlock启动事务块
EndTransactionBlock结束事务块
UserAbortTransactionBlock用户显示结束事务块
DefineSavepoint生成保存点
RollbackToSavepoint回滚到某保存点
ReleaseSavepoint释放保存点

中层事务函数,每个sql在执行前后都会调用中层事务函数,包括检测到异常后,有如下函数

StartTransactionCommand启动事务命令
CommitTransactionCommand完成事务命令,注意不是提交命令
AbortCurrentTransaction退出当前事务

底层事务函数,真正的事务处理函数,负责维护事务状态、事务资源分配和回收等,有如下函数

StartTransaction启动事务
CommitTransaction提交事务
AbortTransaction回滚/中断事务
CleanupTransaction清理事务
StartSubTransaction启动子事务
CommitSubTransaction提交子事务
AbortSubTransaction回滚/中断子事务
CleanupSubTransaction清理子事务

其实这上面几个函数还是比较好分辨的。抛开几个特殊的函数(上层相关savepoint,中层abort函数),其实上、中、下三层事务层分成了:*Block(事务块函数),*Command(command函数),*Transaction(真正的事务处理函数)。然后把savepoint子事务当做事务块函数(后面会介绍,子事务可以在事务块中回退,所以这里把子事务放在事务块一级理所当然),把abort命令当做command级函数就可以了。

事务块状态

上层函数和中层函数同时控制事务块状态,底层函数控制事务状态

事务块状态和事务状态均在src/backend/access/transam/xact.c

typedef enum TBlockState
{
/* 不在事务块中的状态 */
TBLOCK_DEFAULT,                /* 空闲状态,事务开始或结束后都处于此状态 */
TBLOCK_STARTED,                /* 刚开始进入事务块时的状态,由TBLOCK_DEFAULT转换到此状态,此状态存在时间较短 */
 
/* 事务块状态 */
TBLOCK_BEGIN,                /* 启动事务块,此时才启动数据块,进入数据块级状态 */
TBLOCK_INPROGRESS,            /* 活跃的事务,BEGIN以后事务块一直处于此状态,直到事务结束 */
TBLOCK_IMPLICIT_INPROGRESS, /* 隐式BEGIN的活跃事务 */
TBLOCK_PARALLEL_INPROGRESS, /* 并行执行的活跃事务 */
TBLOCK_END,                    /* 收到COMMIT命令 */
TBLOCK_ABORT,                /* 事务失败,等待ROLLBACK */
TBLOCK_ABORT_END,            /* 事务失败,收到ROLLBACK */
TBLOCK_ABORT_PENDING,        /* 活跃事务,收到ROLLBACK */
TBLOCK_PREPARE,                /* 活跃事务,收到PREPARE(显式2PC) */

/* 子事务状态(仍然是事务块级状态) */
TBLOCK_SUBBEGIN,            /* 启动子事务 */
TBLOCK_SUBINPROGRESS,        /* 活跃的子事务 */
TBLOCK_SUBRELEASE,            /* 收到RELEASE(释放保存点) */
TBLOCK_SUBCOMMIT,            /* 当子事务还在运行的时候(SUBINPROGRESS),收到父事务COMMIT */
TBLOCK_SUBABORT,            /* 失败子事务,等待rollback命令 */
TBLOCK_SUBABORT_END,        /* 失败子事务,收到rollback命令 */
TBLOCK_SUBABORT_PENDING,    /* 活跃子事务,收到rollback命令 */
TBLOCK_SUBRESTART,            /* 活跃子事务,收到rollback to命令 */
TBLOCK_SUBABORT_RESTART        /* 失败子事务,收到ROLLBACK TO命令 */
} TBlockState;

大部分状态是显而易见的。需要补充说明的是事务回滚(rollback)和事务失败(ABORT)两者后续行为是相似的,他们都要把清理事务资源和退出当前事务。但是pg把他们分为了两种行为,设置了两种状态TBLOCK_ABORTTBLOCK_ABORT_END(子事务亦然),为什么会这样呢?

src/backend/access/transam/README中对此现象作了详细的说明:

场景 1场景 2
1) 用户输入 BEGIN1) 用户输入 BEGIN
2) 用户执行某些命令2) 用户执行某些命令
3) 用户不喜欢她所看到的东西,输入ABORT3) 事务系统因为某些原因中断(语法错误等)

场景1中,我们想中断事务并把事务回退到default状态 。

场景2中,可能后续还会有更多的命令,这些命令也是当前事务块的一部分,我们不得不忽略这些命令,直到我们看见COMMIT or ROLLBACK

AbortCurrentTransaction处理事务内部中断,UserAbortTransactionBlock处理事务用户中断。两个函数都依赖AbortTransaction来处理所有真正的工作。唯一区别在于AbortTransaction工作结束后我们进入了什么状态:

* AbortCurrentTransaction leaves us in TBLOCK_ABORT

* UserAbortTransactionBlock leaves us in TBLOCK_ABORT_END(原文如此,不过用户输入结束应该进入TBLOCK_ABORT_PENDING状态

底层事务中断处理分为两个阶段:

* 一旦我们意识到事务失败,就会执行AbortTransaction。这应该释放所有共享资源(锁等),以防不必要的增加其他backends的延迟。

* 当我最终看到用户COMMIT或者ROLLBACK时,执行CleanupTransaction;该函数将清理资源并让我们完全跳出事务。特别是,在此之前我们不能破坏TopTransactionContext

事务状态

事务状态一目了然(注意跟事务块状态是不同的)

typedef enum TransState
{
TRANS_DEFAULT,                /* 空闲 */
TRANS_START,                /* 事务启动 */
TRANS_INPROGRESS,            /* 活跃的事务 */
TRANS_COMMIT,                /* 事务提交 */
TRANS_ABORT,                /* 退出事务 */
TRANS_PREPARE                /* prepare事务(2pc) */
} TransState;

事务状态流转

事务块中的一个个命令,调用事务函数,事务函数转变事务、事务块的状态
以一个最简单的事务块举例(参考readme)

1)BEGIN
2)SELECT * FROM foo
3)INSERT INTO foo VALUES (...)
4)COMMIT

命令调用关系:

  	/  StartTransactionCommand;    --中层事务命令启动函数
   /    StartTransaction;        --底层真正处理启动事务函数
1)<  ProcessUtility;         --ProcessUtility处理BEGIN命
   \    BeginTransactionBlock;     --顶层事务块启动函数
    \ CommitTransactionCommand;    --中层完成命令函数  
		 
    /  StartTransactionCommand;    --中层事务命令启动函数
2) /  PortalRunSelect;        --SELECT语句执行
   \  CommitTransactionCommand;    --中层完成命令函数
    \    CommandCounterIncrement;    --中层完成命令函数
    
    /  StartTransactionCommand;    --中层事务命令启动函数
3) /  ProcessQuery;          --INSERT语句执行
   \  CommitTransactionCommand;    --中层完成命令函数
    \    CommandCounterIncrement;    --命令计数器计数+1

      / StartTransactionCommand;    --中层事务命令启动函数
     /  ProcessUtility;         --ProcessUtility处理commit命令
 4) <    EndTransactionBlock;      --调用顶层事务块结束函数
     \  CommitTransactionCommand;    --中层完成命令函数
      \   CommitTransaction;       --底层真正处理提交事务函数
  • 事务块的每一个命令,都会以中层函数StartTransactionCommandCommitTransactionCommand开始和结束
  • 在以上两个中层函数中间,可以看成真正执行的命令处理

2)SELECT和3)INSERT事务块状态都是TBLOCK_INPROGRESSBEGINCOMMIT状态块转换流程如下:

在这里插入图片描述

事务函数参考

《postgresql技术内幕》
src/backend/access/transam/README

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

liuzhilongDBA

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

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

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

打赏作者

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

抵扣说明:

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

余额充值