ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(228)- Transaction(事务系统简介)

PostgreSQL 源码解读(228)- Transaction(事务系统简介)

原创 PostgreSQL 作者:husthxd 时间:2019-09-18 15:47:18 0 删除 编辑

本节是PostgreSQL 事务管理的简要说明,翻译自README文件.

一、The Transaction System

README

src/backend/access/transam/README
The Transaction System
======================
事务系统
PostgreSQL's transaction system is a three-layer system.  The bottom layer
implements low-level transactions and subtransactions, on top of which rests
the mainloop's control code, which in turn implements user-visible
transactions and savepoints.
PostgreSQL的事务分为3层,底层实现了低层次的事务和子事务,在其顶上驻留主循环控制代码,
而主循环实现了用户可见性事务和保存点.
The middle layer of code is called by postgres.c before and after the
processing of each query, or after detecting an error:
        StartTransactionCommand
        CommitTransactionCommand
        AbortCurrentTransaction
Meanwhile, the user can alter the system's state by issuing the SQL commands
BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ROLLBACK TO or RELEASE.  The traffic cop
redirects these calls to the toplevel routines
        BeginTransactionBlock
        EndTransactionBlock
        UserAbortTransactionBlock
        DefineSavepoint
        RollbackToSavepoint
        ReleaseSavepoint
respectively.  Depending on the current state of the system, these functions
call low level functions to activate the real transaction system:
        StartTransaction
        CommitTransaction
        AbortTransaction
        CleanupTransaction
        StartSubTransaction
        CommitSubTransaction
        AbortSubTransaction
        CleanupSubTransaction
在处理查询的前后或者检测到错误时,postgres.c会调用中间层的代码:
        StartTransactionCommand
        CommitTransactionCommand
        AbortCurrentTransaction
在此期间,通过执行BEGIN/COMMIT/ROLLBACK/SAVEPOINT/ROLLBACK TO/RELEASE命令改变系统状态.
调度程序会把这些调用重定向至相应的顶层例程上.
        BeginTransactionBlock
        EndTransactionBlock
        UserAbortTransactionBlock
        DefineSavepoint
        RollbackToSavepoint
        ReleaseSavepoint
依赖于当前的系统状态,这些函数调用底层函数激活真正的事务系统:
        StartTransaction
        CommitTransaction
        AbortTransaction
        CleanupTransaction
        StartSubTransaction
        CommitSubTransaction
        AbortSubTransaction
        CleanupSubTransaction
Additionally, within a transaction, CommandCounterIncrement is called to
increment the command counter, which allows future commands to "see" the
effects of previous commands within the same transaction.  Note that this is
done automatically by CommitTransactionCommand after each query inside a
transaction block, but some utility functions also do it internally to allow
some operations (usually in the system catalogs) to be seen by future
operations in the same utility command.  (For example, in DefineRelation it is
done after creating the heap so the pg_class row is visible, to be able to
lock it.)
另外,在事务内,调用CommandCounterIncrement增加命令计数,这可以让未来的命令可以看到
在同一个事务中先前命令的影响.
注意该动作由CommitTransactionCommand在事务块内部完成每个查询后自动完成,
但某些工具函数同样会内部实现此功能以允许某些操作(通常在系统目录中)可被未来同样的工具命令看到.
(比如,在DefineRelation,在创建堆后已完成,因此pg_class中的行已可见,并能执行锁定)
For example, consider the following sequence of user commands:
举个例子,考虑下面一组用户命令:
1)        BEGIN
2)        SELECT * FROM foo
3)        INSERT INTO foo VALUES (...)
4)        COMMIT
In the main processing loop, this results in the following function call
sequence:
在主处理循环,会形成下面函数调用序列:
     /  StartTransactionCommand;     -- middle
    /       StartTransaction;         -- bottom
1) <    ProcessUtility;                 << BEGIN
    \       BeginTransactionBlock;    -- top
     \  CommitTransactionCommand;    -- middle
    /   StartTransactionCommand;    -- middle
2) /    PortalRunSelect;                << SELECT ...
   \    CommitTransactionCommand;    -- middle
    \       CommandCounterIncrement;
    /   StartTransactionCommand;    -- middle
3) /    ProcessQuery;                   << INSERT ...
   \    CommitTransactionCommand;    -- middle
    \       CommandCounterIncrement;
     /  StartTransactionCommand;    -- middle
    /   ProcessUtility;                 << COMMIT
4) <        EndTransactionBlock;    -- top
    \   CommitTransactionCommand;    -- middle
     \      CommitTransaction;        -- bottom
The point of this example is to demonstrate the need for
StartTransactionCommand and CommitTransactionCommand to be state smart -- they
should call CommandCounterIncrement between the calls to BeginTransactionBlock
and EndTransactionBlock and outside these calls they need to do normal start,
commit or abort processing.
该例子想表达的意思是StartTransactionCommand和CommitTransactionCommand需要具备状态智能
-- 在BeginTransactionBlock/EndTransactionBlock之间需调用CommandCounterIncrement,
在这些调用之外,它们需要执行常规的start,commit或abort处理.
Furthermore, suppose the "SELECT * FROM foo" caused an abort condition. In
this case AbortCurrentTransaction is called, and the transaction is put in
aborted state.  In this state, any user input is ignored except for
transaction-termination statements, or ROLLBACK TO <savepoint> commands.
而且,假定"SELECT * FROM foo"出错,导致需要abort,那么会调用AbortCurrentTransaction(bottom),
事务状态为aborted状态.事务处于这个状态,除了事务终止语句或者ROLLBACK TO <savepoint>命令外,所有用户输入都会被忽略.
Transaction aborts can occur in two ways:
事务取消的情况有两种:
1) system dies from some internal cause  (syntax error, etc)
   内部原因,如语法错误等.
2) user types ROLLBACK
   用户类型的ROLLBACK.
The reason we have to distinguish them is illustrated by the following two
situations:
区分事务取消的原因如下两例所示:
        case 1                                  case 2
        ------                                  ------
1) user types BEGIN                     1) user types BEGIN
2) user does something                  2) user does something
3) user does not like what              3) system aborts for some reason
   she sees and types ABORT                (syntax error, etc)
In case 1, we want to abort the transaction and return to the default state.
In case 2, there may be more commands coming our way which are part of the
same transaction block; we have to ignore these commands until we see a COMMIT
or ROLLBACK.
第一种情况,用户希望取消事务并返回到默认状态.
第二种情况,在同一个事务块中,可能会有更多的命令进入,需要忽略这些命令直至COMMIT/ROLLBACK.
Internal aborts are handled by AbortCurrentTransaction, while user aborts are
handled by UserAbortTransactionBlock.  Both of them rely on AbortTransaction
to do all the real work.  The only difference is what state we enter after
AbortTransaction does its work:
* AbortCurrentTransaction leaves us in TBLOCK_ABORT,
* UserAbortTransactionBlock leaves us in TBLOCK_ABORT_END
内部的事务取消通过AbortCurrentTransaction(bottom)处理,而用户取消通过UserAbortTransactionBlock(top)处理.
它们都需要依赖AbortTransaction(bottom)来处理实际的工作,不同的地方是在AbortTransaction后进入的状态不同:
* AbortCurrentTransaction进入TBLOCK_ABORT
* UserAbortTransactionBlock进入TBLOCK_ABORT_END
Low-level transaction abort handling is divided in two phases:
* AbortTransaction executes as soon as we realize the transaction has
  failed.  It should release all shared resources (locks etc) so that we do
  not delay other backends unnecessarily.
* CleanupTransaction executes when we finally see a user COMMIT
  or ROLLBACK command; it cleans things up and gets us out of the transaction
  completely.  In particular, we mustn't destroy TopTransactionContext until
  this point.
底层事务取消处理分为两个阶段:
* 一旦感知事务已失败,则马上执行AbortTransaction,需要释放所有的共享资源(比如锁等)以便不影响其他后台进程.
* 在用户发出COMMIT/ROLLBACK时执行CleanupTransaction;清理现场并完整的跳出事务.
  特别地,在这个点上才需要销毁TopTransactionContext
Also, note that when a transaction is committed, we don't close it right away.
Rather it's put in TBLOCK_END state, which means that when
CommitTransactionCommand is called after the query has finished processing,
the transaction has to be closed.  The distinction is subtle but important,
because it means that control will leave the xact.c code with the transaction
open, and the main loop will be able to keep processing inside the same
transaction.  So, in a sense, transaction commit is also handled in two
phases, the first at EndTransactionBlock and the second at
CommitTransactionCommand (which is where CommitTransaction is actually
called).
同时,注意如果事务已提交,必须要马上关闭,而是进入TBLOCK_END状态,
这意味着在查询完成后执行CommitTransactionCommand,事务才会关闭.
这种区别很微妙,但很重要,因为控制已在事务开启的情况下从xact.c代码中跳出,主循环仍在相同的主事务中.
因此,在某种意义上来说,事务提交存在两个阶段,首先EndTransactionBlock(top),其次CommitTransactionCommand(middle).
(CommitTransactionCommand是实际调用CommitTransaction的地方)
The rest of the code in xact.c are routines to support the creation and
finishing of transactions and subtransactions.  For example, AtStart_Memory
takes care of initializing the memory subsystem at main transaction start.
xact.c的剩余代码是用于支持创建和结束事务和子事务的例程.
比如AtStart_Memory在主事务开启时处理初始化内存子系统.

TransactionState结构体


/*
 *    transaction states - transaction state from server perspective
 */
typedef enum TransState
{
    TRANS_DEFAULT,                /* idle */
    TRANS_START,                /* transaction starting */
    TRANS_INPROGRESS,            /* inside a valid transaction */
    TRANS_COMMIT,                /* commit in progress */
    TRANS_ABORT,                /* abort in progress */
    TRANS_PREPARE                /* prepare in progress */
} TransState;
/*
 *    transaction block states - transaction state of client queries
 *
 * Note: the subtransaction states are used only for non-topmost
 * transactions; the others appear only in the topmost transaction.
 */
typedef enum TBlockState
{
    /* not-in-transaction-block states */
    TBLOCK_DEFAULT,                /* idle */
    TBLOCK_STARTED,                /* running single-query transaction */
    /* transaction block states */
    TBLOCK_BEGIN,                /* starting transaction block */
    TBLOCK_INPROGRESS,            /* live transaction */
    TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN */
    TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker */
    TBLOCK_END,                    /* COMMIT received */
    TBLOCK_ABORT,                /* failed xact, awaiting ROLLBACK */
    TBLOCK_ABORT_END,            /* failed xact, ROLLBACK received */
    TBLOCK_ABORT_PENDING,        /* live xact, ROLLBACK received */
    TBLOCK_PREPARE,                /* live xact, PREPARE received */
    /* subtransaction states */
    TBLOCK_SUBBEGIN,            /* starting a subtransaction */
    TBLOCK_SUBINPROGRESS,        /* live subtransaction */
    TBLOCK_SUBRELEASE,            /* RELEASE received */
    TBLOCK_SUBCOMMIT,            /* COMMIT received while TBLOCK_SUBINPROGRESS */
    TBLOCK_SUBABORT,            /* failed subxact, awaiting ROLLBACK */
    TBLOCK_SUBABORT_END,        /* failed subxact, ROLLBACK received */
    TBLOCK_SUBABORT_PENDING,    /* live subxact, ROLLBACK received */
    TBLOCK_SUBRESTART,            /* live subxact, ROLLBACK TO received */
    TBLOCK_SUBABORT_RESTART        /* failed subxact, ROLLBACK TO received */
} TBlockState;
/*
 *    transaction state structure
 */
typedef struct TransactionStateData
{
    FullTransactionId fullTransactionId;    /* my FullTransactionId */
    SubTransactionId subTransactionId;    /* my subxact ID */
    char       *name;            /* savepoint name, if any */
    int            savepointLevel; /* savepoint level */
    TransState    state;            /* low-level state */
    TBlockState blockState;        /* high-level state */
    int            nestingLevel;    /* transaction nesting depth */
    int            gucNestLevel;    /* GUC context nesting depth */
    MemoryContext curTransactionContext;    /* my xact-lifetime context */
    ResourceOwner curTransactionOwner;    /* my query resources */
    TransactionId *childXids;    /* subcommitted child XIDs, in XID order */
    int            nChildXids;        /* # of subcommitted child XIDs */
    int            maxChildXids;    /* allocated size of childXids[] */
    Oid            prevUser;        /* previous CurrentUserId setting */
    int            prevSecContext; /* previous SecurityRestrictionContext */
    bool        prevXactReadOnly;    /* entry-time xact r/o state */
    bool        startedInRecovery;    /* did we start in recovery? */
    bool        didLogXid;        /* has xid been included in WAL record? */
    int            parallelModeLevel;    /* Enter/ExitParallelMode counter */
    bool        chain;            /* start a new block after this one */
    struct TransactionStateData *parent;    /* back link to parent */
} TransactionStateData;
typedef TransactionStateData *TransactionState;

二、参考资料

README

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2657423/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。现就职于广州云图数据技术有限公司,系统架构师。

注册时间:2007-12-28

  • 博文量
    1394
  • 访问量
    3841741