开源 OpenGauss数据库中事务管理源码解析

一、 事务状态机

openGauss 将事务系统分为上层(事务块 TBlockState)以及底层(TransState)两个层次。(代码位于 src/gausskernel/storage/access/transam/xact.cpp)

  1. 上层事务块:客户端 query 的状态,用于提高用户操作数据的灵活性,用事务块的形式支持在一个事务中执行多条 query 语句。事务块状态机: 每个事务状态都对应一个事务状态机结构体。

    在无异常情形下,一个事务块的状态机如上图所示按照默认(TBLOCK_DEFAULT)->已开始(TBLOCK_STARTED)->事务块开启(TBLOCK_BEGIN)->事务块运行中(TBLOCK_INPROGRESS)->事务块结束(TBLOCK_END)->默认(TBLOCK_DEFAULT)循环。剩余的状态机是在上述正常场景下的各个状态点的异常处理分支。

    在事务块运行中(TBLOCK_INPROGRESS)出错分为 2 种情形。事务执行失败:事务块运行中(TBLOCK_INPROGRESS)->回滚(TBLOCK_ABORT)->回滚结束(TBLOCK_ABORT_END)->默认(TBLOCK_DEFAULT);用户手动回滚执行成功的事务:事务块运行中(TBLOCK_INPROGRESS)->回滚等待(TBLOCK_ABORT_PENDING)->默认(TBLOCK_DEFAULT)。

  2. 底层(TransState):从内核视角的事务状态,真正意义上的事务状态。事务底层状态:

    内核内部底层状态如上图所示,底层状态机的描述见结构体 TransState。在事务开启前事务状态为 TRANS_DEFAULT。

    事务开启过程中事务状态为 TRANS_START。

    事务成功开启后一直处于 TRANS_INPROGRESS。

    事务结束/回滚的过程中为 TARNS_COMMIT/ TRANS_ABORT。

    事务结束后事务状态回到 TRANS_DEFAULT。

二、 事务状态机系统实例

 BEGIN;SELECT * FROM TABLE1; END;
  1. 整体流程

    任何语句的执行总是先进入事务处理接口事务块中,然后调用事务底层函数处理具体命令,最后返回到事务块中。

  2. BEGIN 执行流程

    (1) 通过入口函数 exec_simple_query 处理 begin 命令。(函数位于\openGauss-server-master\src\gausskernel\process\tcop\postgres.cpp)

    static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)

    该函数添加默认参数 unint16 messageType。其默认值为 0。如果我们收到混合消息,该参数将设置为 1,以告诉我们正常查询字符串后跟信息字符串。query_string = normal querystring + message

    begin 的 query_string=0x1002cd0”begin;”

    然后我们将 meesageType 与 1 进行比较。当 messageType 为 1 时,表示此消息是混合消息。查询字符串后面是一些我们必须处理的信息。我们将查询字符串分为两部分,普通查询字符串和信息字符串。然后主要调用start_xact_command();

    (void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag);

    finish_xact_command();

    (2) start_xact_command 函数开始一个 query 命令,调用 StartTransactionCommand 函数,此时事务块上层状态未 TBLOCK_DEFAULT,继续调用 StartTransaction 函数,设置事务底层状态 TRANS_START,完成内存、缓存区、锁资源的初始化后将事务底层状态设为 TRANS_INPROGRESS,最后在 StartTransactionCommand 函数中设置事务块上层状态为 TBLOCK_STARTED。 TBLOCK_DEFAULT->TBLOCK_STARTED

    TRANS_DEFAULT->TRANS_START->TRANS_INPROGRESS

    TBLOCK_DEFAULT->TBLOCK_STARTED

    TRANS_DEFAULT->TRANS_START->TRANS_INPROGRESS

    TBLOCK_DEFAULT->TBLOCK_STARTED

    TRANS_DEFAULT->TRANS_START->TRANS_INPROGRESS

    TBLOCK_DEFAULT->TBLOCK_STARTED

(3) PortalRun 处理 begin 语句(4) finish_xact_command finish_xact_command 函数结束一个 query 命令,调用 CommitTransactionCommand 函数设置事务块上层状态从 TBLOCK_BEGIN 变为 TBLOCK_INPROGRESS,并等待读取下一条命令。
  1. Select 语句流程

    (1) exec_simple_query select 的 query_string=0x1002cd0”SELECT * FROM table1;”

    (2) start_xact_command 调用 StartTransactionCommand 函数,由于当前上层事务块状态为 TBLOCK_INPROGRESS,说明已经在事务块内部,则直接返回,不改变事务上层以及底层的状态。

    (3) PortalRun:依次向下调用函数 ExecutorRun 根据执行计划执行最优路径查询。 (4) finish_xact_command 调用 CommitTransactionCommand 函数,当前事务块上层状态仍为 TBLOCK_INPROGESS,不改变当前事务上层以及底层的状态。

  2. Begin 语句流程

    (1) exec_simple_query end 的 query_string=0x1002cd0”end;”

    (2) start_xact_command 调用 StartTransactionCommand 函数,由于当前上层事务块状态为 TBLOCK_INPROGRESS,说明已经在事务块内部,则直接返回,不改变事务上层以及底层的状态。

    (3) PortalRun 设置事务块上层状态为 TBLOCK_END。 (4) finish_xact_command 设置事务底层状态为 TRANS_COMMIT,进行事务提交流程并且清理事务资源;清理后设置底层事务状态为 TRANS_DEFAULT继续调用 CommitTransaction 函数提交事务,设置事务底层状态为 TRANS_COMMIT,进行事务提交流程并且清理事务资源;本地持久化 CLOG 及 XLOG日志,并清空相应的事务槽位信息。清理后设置底层事务状态为 TRANS_DEFAULT,返回 CommitTansactionCommand 函数;设置事务块上层状态为 TBLOCK_DEFAULT,整个事务块结束。