BEGIN-END-ABORT

海山数据库(He3DB)源码详解:事务块执行过程及内核函数

本文介绍了事务执行过程中,从BEGIN事务开始到事务结束END/ABORT的内核函数执行过程。

前言:

事务SQL语句的执行过程,会先通过事务块函数修改事务块状态,然后根据事务块状态执行不同的事务行为。因此,本章会讲解事务块的开始和结束过程的事务块函数。

事务块控制事务执行的过程:

在讲具体函数之前,有必要在此讲解一下,事务块是如何控制事务行为的。事务管理器中,事务块会根据当前事务状态,来控制具体的事务操作行为,具体流程如下:
在这里插入图片描述

  • 开始事务块:表示一个新的事务块的开始。
  • 检查事务状态:检查当前事务的状态,是否活跃、空闲或处于错误状态。
    • 活跃:如果事务处于活跃状态,执行事务操作。
      • 执行事务操作:执行具体的事务操作,如插入、更新或删除数据。
    • 空闲:如果事务处于空闲状态,等待操作命令。
      • 等待操作命令:在事务空闲时等待接收到新的操作命令。
      • 接收到操作命令:一旦接收到操作命令,开始执行事务操作。
    • 执行事务操作:执行具体的事务操作,如插入、更新或删除数据。
      • 操作成功?:检查事务操作是否成功。
      • 提交事务:如果操作成功,提交事务。
      • 回滚事务:如果操作失败或事务处于错误状态,回滚事务。
    • 错误:如果事务处于错误状态,进行回滚操作。
      • 回滚事务:如果操作失败或事务处于错误状态,回滚事务。
  • 结束事务块:表示事务块的结束,此时事务已经提交或回滚。

如何进行事务块操作:

使用 BEGIN 命令可以手动控制启动事务的边界,开始一个新的事务块;
使用 END \COMMIT 命令可以手动控制结束事务,结束一个执行中的事务块;
使用 ABORT \ ROLLBACK 命令可以手动控制放弃回滚事务,结束一个出错的事务块;
当执行这些SQL语句时,系统底层会调用相对应的事务块函数,来修改当前事务块的状态,从而控制具体的事务操作行为。其中,事务块操作的开始和结束分别对应的函数为:

  1. BEGIN 对应的事务块函数为 BeginTransactionBlock() 函数;
  2. END \ COMMIT 对应的事务块函数为 EndTransactionBlock() 函数;
  3. ABORT \ ROLLBACK 对应的事务块函数为 UserAbortTransactionBlock() 函数。
    那么,这些函数的执行流程是什么样的?

BeginTransactionBlock()函数的执行过程:

当终端输入BEGIN或者START TRANSACTION命令时,会开始一个事务块。处理过程首先会调用BeginTransactionBlock()函数。该函数内部主体为一个switch结构,会对枚举类型TBlockState中的所有状态都进行一个判断,并作一个对应的处理。执行过程示意图如下:

在这里插入图片描述

  1. 首先通过全局变量CurrentTransactionState获得当前事务块状态的变量;

     TransactionState s = CurrentTransactionState;
    
  2. 然后通过switch-case结构,根据当前事务块状态做对应的操作,其中有包括:

  • 如果当前事务块状态为TBLOCK_STARTED,则修改为TBLOCK_BEGIN;
        case TBLOCK_STARTED:
    		s->blockState = TBLOCK_BEGIN;
    		break;
    
  • 如果当前事务块状态为TBLOCK_IMPLICIT_INPROGRESS,则修改为TBLOCK_BEGIN;
        case TBLOCK_IMPLICIT_INPROGRESS:
    		s->blockState = TBLOCK_BEGIN;
    		break;
    
  • 如果当前事务块状态为TBLOCK_INPROGRESS、TBLOCK_PARALLEL_INPROGRESS、TBLOCK_SUBINPROGRESS、TBLOCK_ABORT和TBLOCK_SUBABORT,会进行WARNING报警,但是不会影响执行过程,可以继续执行;
        case TBLOCK_INPROGRESS:
    	case TBLOCK_PARALLEL_INPROGRESS:
    	case TBLOCK_SUBINPROGRESS:
    	case TBLOCK_ABORT:
    	case TBLOCK_SUBABORT:
    		ereport(WARNING,
    				(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
    				 errmsg("there is already a transaction in progress")));
    		break;
    
  • 如果当前事务块状态为上述提到的其他状态,则直接报FATAL错误,直接退出事务块过程;
        case TBLOCK_DEFAULT:
    	case TBLOCK_BEGIN:
    	case TBLOCK_SUBBEGIN:
    	case TBLOCK_END:
    	case TBLOCK_SUBRELEASE:
    	case TBLOCK_SUBCOMMIT:
    	case TBLOCK_ABORT_END:
    	case TBLOCK_SUBABORT_END:
    	case TBLOCK_ABORT_PENDING:
    	case TBLOCK_SUBABORT_PENDING:
    	case TBLOCK_SUBRESTART:
    	case TBLOCK_SUBABORT_RESTART:
    	case TBLOCK_PREPARE:
    		elog(FATAL, "BeginTransactionBlock: unexpected state %s",
    			 BlockStateAsString(s->blockState));
    		break;
    

以上,就是BeginTransactionBlock()函数的全部执行过程。

EndTransactionBlock()函数的执行过程:

当终端输入COMMIT或者EDN语句时,这时一个事务块会发起提交或者结束,如果输入END,正确结束为提交,错误结束为回滚。处理过程首先会调用EndTransactionBlock()函数,并根据返回值判断是否时错误结束发生回滚。EndTransactionBlock()函数和BeginTransactionBlock()十分相似,内部主要是一个swith结构,这里直接说执行流程:
在这里插入图片描述

  1. 首先通过全局变量CurrentTransactionState获得当前事务块状态的变量,并定义返回值bool变量result;

     TransactionState s = CurrentTransactionState;
     bool		result = false;
    
  2. 然后通过switch-case结构,根据当前事务块状态做对应的操作,其中有包括:

  • 如果当前事务块状态为TBLOCK_INPROGRESS,则修改为TBLOCK_END,并将变量result赋值为true;
     case TBLOCK_INPROGRESS:
         s->blockState = TBLOCK_END;
         result = true;
         break;
    
  • 如果当前事务块状态为TBLOCK_IMPLICIT_INPROGRESS,表示当前处在一个隐式的事务进程模式。判断入参chain的值,如果为true,表示该状态下存在子事务,报Error错误,退出事务执行过程;否则报Warning,修改事务块状态为TBLOCK_END,并将变量result赋值为true;
     case TBLOCK_IMPLICIT_INPROGRESS:
         if (chain)
             ereport(ERROR,
                     (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
             /* translator: %s represents an SQL statement name */
                         errmsg("%s can only be used in transaction blocks",
                             "COMMIT AND CHAIN")));
         else
             ereport(WARNING,
                     (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
                         errmsg("there is no transaction in progress")));
         s->blockState = TBLOCK_END;
         result = true;
         break;
    
  • 如果当前事务块状态为TBLOCK_ABORT,只修改事务块状态为TBLOCK_ABORT_END;
     case TBLOCK_ABORT:
         s->blockState = TBLOCK_ABORT_END;
         break;
    
  • 如果当前事务块状态为TBLOCK_SUBINPROGRESS,表示当前事务属于正常状态可以提交,需要从当前节点逐级回溯到父节点,并修改过程中每个节点的状态;
     case TBLOCK_SUBINPROGRESS:
         while (s->parent != NULL)
         {
             if (s->blockState == TBLOCK_SUBINPROGRESS)
                 s->blockState = TBLOCK_SUBCOMMIT;
             else
                 elog(FATAL, "EndTransactionBlock: unexpected state %s",
                         BlockStateAsString(s->blockState));
             s = s->parent;
         }
         if (s->blockState == TBLOCK_INPROGRESS)
             s->blockState = TBLOCK_END;
         else
             elog(FATAL, "EndTransactionBlock: unexpected state %s",
                     BlockStateAsString(s->blockState));
         result = true;
         break;
    
    • 首先,通过while循环,回溯每个节点,并对每个节点的事务块状态进行判断。如果是TBLOCK_SUBINPROGRESS,则修改为TBLOCK_SUBCOMMIT,由后续的CommitSubTransaction()函数中进行提交,如果是其他,则直接报FATAL错误,退出事务操作过程;
    • 退出while后,判断父节点事务块状态,如果是TBLOCK_INPROGRESS,则修改为TBLOCK_END,表示事务完成,后续由CommitTransaction()函数完成具体的事务提交,如果是其他,报FATAL错误;
    • 最后修改返回值result为true。
  • 如果当前事务块状态为TBLOCK_SUBABORT,表示当前事务整体处于放弃状态,需要从当前节点逐级回溯到父节点,并修改过程中每个节点的状态以实现整体事务的放弃;
     case TBLOCK_SUBABORT:
         while (s->parent != NULL)
         {
             if (s->blockState == TBLOCK_SUBINPROGRESS)
                 s->blockState = TBLOCK_SUBABORT_PENDING;
             else if (s->blockState == TBLOCK_SUBABORT)
                 s->blockState = TBLOCK_SUBABORT_END;
             else
                 elog(FATAL, "EndTransactionBlock: unexpected state %s",
                         BlockStateAsString(s->blockState));
             s = s->parent;
         }
         if (s->blockState == TBLOCK_INPROGRESS)
             s->blockState = TBLOCK_ABORT_PENDING;
         else if (s->blockState == TBLOCK_ABORT)
             s->blockState = TBLOCK_ABORT_END;
         else
             elog(FATAL, "EndTransactionBlock: unexpected state %s",
                     BlockStateAsString(s->blockState));
         break;
    
    • 首先,通过while循环,回溯每个节点,并对每个节点的事务块状态进行判断。如果是TBLOCK_SUBINPROGRESS,则修改为TBLOCK_SUBABORT_PENDING,表示处于准备放弃的状态,后续会通过AbortSubTransaction()和CleanupSubTransaction()函数处理;如果是TBLOCK_SUBABORT,则修改为TBLOCK_SUBABORT_END,表示处于结束放弃的状态,已经由AbortSubTransaction()函数处理过,后续只需要通过CleanupSubTransaction()清理相关资源即可;如果是其他,则直接报FATAL错误,退出事务操作过程;
    • 退出while后,判断父节点事务块状态,如果是TBLOCK_INPROGRESS,则修改为TBLOCK_ABORT_PENDING,表示事务准备放弃,后续由AbortTransaction()和CleanupTransaction()函数处理;如果是TBLOCK_ABORT,则修改为TBLOCK_ABORT_END,表示处于结束放弃的状态,已经由AbortTransaction()函数处理过,后续只需要通过CleanupTransaction()清理相关资源即可;如果是其他,报FATAL错误。
  • 如果当前事务块状态为TBLOCK_STARTED,表示当前事务执行出错,需要通过chain判断错误的严重性。判断入参chain的值,如果为true,表示当前状态下存在子事务,报Error错误,退出事务执行过程;否则报Warning,并将变量result赋值为true;
     if (chain)
         ereport(ERROR,
                 (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
         /* translator: %s represents an SQL statement name */
                     errmsg("%s can only be used in transaction blocks",
                         "COMMIT AND CHAIN")));
     else
         ereport(WARNING,
                 (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
                     errmsg("there is no transaction in progress")));
     result = true;
     break;
    
  • 如果当前事务块状态为TBLOCK_PARALLEL_INPROGRESS,表示当前事务处于并行操作的状态,不能有parallel worker进行提交,报FATAL错误,直接退出事务操作过程;
     case TBLOCK_PARALLEL_INPROGRESS:
         ereport(FATAL,
                 (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
                     errmsg("cannot commit during a parallel operation")));
         break;
    
  • 如果处于其他状态,则表示当前事务状态出现预料之外的情况,报FATAL错误,直接退出事务操作过程。
         /* These cases are invalid. */
     case TBLOCK_DEFAULT:
     case TBLOCK_BEGIN:
     case TBLOCK_SUBBEGIN:
     case TBLOCK_END:
     case TBLOCK_SUBRELEASE:
     case TBLOCK_SUBCOMMIT:
     case TBLOCK_ABORT_END:
     case TBLOCK_SUBABORT_END:
     case TBLOCK_ABORT_PENDING:
     case TBLOCK_SUBABORT_PENDING:
     case TBLOCK_SUBRESTART:
     case TBLOCK_SUBABORT_RESTART:
     case TBLOCK_PREPARE:
         elog(FATAL, "EndTransactionBlock: unexpected state %s",
                 BlockStateAsString(s->blockState));
         break;
    

UserAbortTransactionBlock()函数执行过程:

当终端输入ROLLBACK语句时,这时会发起一个事务的回滚操作,放弃之前的所有操作,数据库回滚到事务开始之前的样子。通过调用UserAbortTransactionBlock()函数执行该过程。该函数和其他事务块操作函数十分相似,内部主要是一个swith结构,这里直接说执行流程:

在这里插入图片描述

  1. 首先通过全局变量CurrentTransactionState获得当前事务块状态的变量;
    TransactionState s = CurrentTransactionState;
  1. 然后通过switch-case结构,根据当前事务块状态做对应的操作,其中有包括:
  • 如果事务块处在TBLOCK_INPROGRESS状态下,将状态修改为TBLOCK_ABORT_PENDING;
	case TBLOCK_INPROGRESS:
        s->blockState = TBLOCK_ABORT_PENDING;
        break;   
  • 如果事务块处在TBLOCK_ABORT状态下,将状态修改为TBLOCK_ABORT_END;
	case TBLOCK_ABORT:
        s->blockState = TBLOCK_ABORT_END;
        break;  
  • 如果事务块处在TBLOCK_SUBINPROGRESS或TBLOCK_SUBABORT,标记每个子事务状态为放弃状态,直到top事务位置;
    case TBLOCK_SUBINPROGRESS:
    case TBLOCK_SUBABORT:
        while (s->parent != NULL)
        {
            if (s->blockState == TBLOCK_SUBINPROGRESS)
                s->blockState = TBLOCK_SUBABORT_PENDING;
            else if (s->blockState == TBLOCK_SUBABORT)
                s->blockState = TBLOCK_SUBABORT_END;
            else
                elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
                        BlockStateAsString(s->blockState));
            s = s->parent;
        }
        if (s->blockState == TBLOCK_INPROGRESS)
            s->blockState = TBLOCK_ABORT_PENDING;
        else if (s->blockState == TBLOCK_ABORT)
            s->blockState = TBLOCK_ABORT_END;
        else
            elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
                    BlockStateAsString(s->blockState));
        break;  
  • 循环检查当前节点的父节点状态,如果不为空,则根据blockState值对应修改当前事务的事务块状态,并将事务指针指向父节点;

  • 当循环到top节点时,退出循环,并根据top节点的blockState值对应修改当前top事务的事务块状态。

  • 如果事务块处在TBLOCK_STARTED或TBLOCK_IMPLICIT_INPROGRESS,根据chain的值报ERROR或者WARING错误,并将事务块状态修改为TBLOCK_ABORT_PENDING;

    case TBLOCK_STARTED:
    case TBLOCK_IMPLICIT_INPROGRESS:
        if (chain)
            ereport(ERROR,
                    (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
            /* translator: %s represents an SQL statement name */
                        errmsg("%s can only be used in transaction blocks",
                            "ROLLBACK AND CHAIN")));
        else
            ereport(WARNING,
                    (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
                        errmsg("there is no transaction in progress")));
        s->blockState = TBLOCK_ABORT_PENDING;
        break;

  • 如果chain为true,表示事务的链式执行,事务在结束后会开启一个新事务,如果开启则汇报ERROR错误;

  • 如果chain为false,报WARIING错误,修改事务块状态。

  • 如果务块处在TBLOCK_PARALLEL_INPROGRESS,则直接报FATAL错误。

    case TBLOCK_PARALLEL_INPROGRESS:
        ereport(FATAL,
                (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
                    errmsg("cannot abort during a parallel operation")));
        break;

原因:事务管理系统设计上避免在并行执行的事务中执行 ABORT,以维护系统的稳定性和数据的一致性。这种设计确保了并行查询的性能和可靠性。

  • 如果事务块处于其他状态,直接FATAL。
    case other_TBLOCK:
        elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
                BlockStateAsString(s->blockState));
        break;
  • 对当前事务块状态做判断,并保存chain的值到
	Assert(s->blockState == TBLOCK_ABORT_END ||
		   s->blockState == TBLOCK_ABORT_PENDING);

	s->chain = chain;
统设计上避免在并行执行的事务中执行 ABORT,以维护系统的稳定性和数据的一致性。这种设计确保了并行查询的性能和可靠性。

- 如果事务块处于其他状态,直接FATAL。
```C 
    case other_TBLOCK:
        elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
                BlockStateAsString(s->blockState));
        break;
  • 对当前事务块状态做判断,并保存chain的值到
	Assert(s->blockState == TBLOCK_ABORT_END ||
		   s->blockState == TBLOCK_ABORT_PENDING);

	s->chain = chain;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值