文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么理解PostgreSQL事务管理中的子事务

2024-04-02 19:55

关注

本篇内容主要讲解“怎么理解PostgreSQL事务管理中的子事务”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解PostgreSQL事务管理中的子事务”吧!

一、Subtransaction Handling

README

Subtransaction Handling
-----------------------
子事务处理
Subtransactions are implemented using a stack of TransactionState structures,
each of which has a pointer to its parent transaction's struct.  When a new
subtransaction is to be opened, PushTransaction is called, which creates a new
TransactionState, with its parent link pointing to the current transaction.
StartSubTransaction is in charge of initializing the new TransactionState to
sane values, and properly initializing other subsystems (AtSubStart routines).
子事务通过TransactionState结构体栈来实现,每一个TransactionState都有指向其父事务结构体的指针.
新的子事务即将开启时,调用PushTransaction,该函数创建TransactionState结构体,parent指向当前事务.
StartSubTransaction负责完整初始化新的TransactionState结构体,并正确初始化其他子系统(AtSubStart例程实现).
When closing a subtransaction, either CommitSubTransaction has to be called
(if the subtransaction is committing), or AbortSubTransaction and
CleanupSubTransaction (if it's aborting).  In either case, PopTransaction is
called so the system returns to the parent transaction.
在关闭子事务时,成功则调用CommitSubTransaction,取消/失败则调用AbortSubTransaction和CleanupSubTransaction.
无论哪种情况,都会调用PopTransaction以便返回到父事务.
One important point regarding subtransaction handling is that several may need
to be closed in response to a single user command.  That's because savepoints
have names, and we allow to commit or rollback a savepoint by name, which is
not necessarily the one that was last opened.  Also a COMMIT or ROLLBACK
command must be able to close out the entire stack.  We handle this by having
the utility command subroutine mark all the state stack entries as commit-
pending or abort-pending, and then when the main loop reaches
CommitTransactionCommand, the real work is done.  The main point of doing
things this way is that if we get an error while popping state stack entries,
the remaining stack entries still show what we need to do to finish up.
子事务处理一个很很重要的点是为了响应单个用户命令需要关闭多个子事务.
这是因为对于命名savepoints,PG允许通过名称提交或回滚至这些savepoint,而这些savepoint并不需要是最后才打开的那个.
同时,COMMIT/ROLLBACK命令必须可以关闭整个栈.
通过工具命令子例程来标记整个状态栈为commit-pending或者是abort-pending,
然后在主循环到达CommitTransactionCommand时,执行实际的commit/abort.
以这种方法做这些事情的主要关注点是如果在栈条目出栈时出现错误,剩余的栈条目仍然可以展示我们需要做什么才能完结.
In the case of ROLLBACK TO <savepoint>, we abort all the subtransactions up
through the one identified by the savepoint name, and then re-create that
subtransaction level with the same name.  So it's a completely new
subtransaction as far as the internals are concerned.
在ROLLBACK TO <savepoint>这种情况中,我们取消了从savepoint name到当前的所有子事务,
然后使用相同的名称重建了该子事务.因此这是内部需要关注的完全的子事务.
Other subsystems are allowed to start "internal" subtransactions, which are
handled by BeginInternalSubTransaction.  This is to allow implementing
exception handling, e.g. in PL/pgSQL.  ReleaseCurrentSubTransaction and
RollbackAndReleaseCurrentSubTransaction allows the subsystem to close said
subtransactions.  The main difference between this and the savepoint/release
path is that we execute the complete state transition immediately in each
subroutine, rather than deferring some work until CommitTransactionCommand.
Another difference is that BeginInternalSubTransaction is allowed when no
explicit transaction block has been established, while DefineSavepoint is not.
其他子系统允许通过BeginInternalSubTransaction函数启动"internal"子事务.
因此可以在诸如PL/pgSQL等编程语言中可以实现异常处理.
ReleaseCurrentSubTransaction和RollbackAndReleaseCurrentSubTransaction允许子系统关闭子事务.
这种方式跟savepoint/release的不同点是在每一个子例程中我们马上执行了完整的状态变换,
而不是等到执行CommitTransactionCommand才执行某些工作.
另外一个不同点是在没有创建显式事务块的情况下允许调用BeginInternalSubTransaction,
而savepoint则需要明确的DefineSavepoint.
Transaction and Subtransaction Numbering
----------------------------------------
事务和子事务编号
Transactions and subtransactions are assigned permanent XIDs only when/if
they first do something that requires one --- typically, insert/update/delete
a tuple, though there are a few other places that need an XID assigned.
If a subtransaction requires an XID, we always first assign one to its
parent.  This maintains the invariant that child transactions have XIDs later
than their parents, which is assumed in a number of places.
事务和子事务在需要时才会分配持久的XIDs,典型的场景是insert/update/delete元组.
如果子事务需要XID,首先会给其父事务分配一个,这保证了子事务编号在父事务之后.
The subsidiary actions of obtaining a lock on the XID and entering it into
pg_subtrans and PG_PROC are done at the time it is assigned.
分配事务号还需要做的事情是在XID获取锁/写入到pg_subtrans和PG_PROC中.
A transaction that has no XID still needs to be identified for various
purposes, notably holding locks.  For this purpose we assign a "virtual
transaction ID" or VXID to each top-level transaction.  VXIDs are formed from
two fields, the backendID and a backend-local counter; this arrangement allows
assignment of a new VXID at transaction start without any contention for
shared memory.  To ensure that a VXID isn't re-used too soon after backend
exit, we store the last local counter value into shared memory at backend
exit, and initialize it from the previous value for the same backendID slot
at backend start.  All these counters go back to zero at shared memory
re-initialization, but that's OK because VXIDs never appear anywhere on-disk.
没有XID的事务仍然需要进行标识,特别是需要持有锁的时候.
出于这个目的,我们分配了"虚拟事务号"(即VXID)给每一个顶层事务.
VXIDs由两个域组成,后台进程ID和后台进程本地计数器;这样的编号产生方法不需要共享内存争用就可以进行新VXID的分配.
为了确保在后台进程退出后VXID不会过快的被使用,我们把最后的本地计数器值存储到共享内存中,
对于同一个后台进程ID,分配先前存储的计数器值给这个新的后台进程.
在共享内存重新初始化后这些计数器会归零,由于不会出现落盘,因此这样的处理没有任何问题.
Internally, a backend needs a way to identify subtransactions whether or not
they have XIDs; but this need only lasts as long as the parent top transaction
endures.  Therefore, we have SubTransactionId, which is somewhat like
CommandId in that it's generated from a counter that we reset at the start of
each top transaction.  The top-level transaction itself has SubTransactionId 1,
and subtransactions have IDs 2 and up.  (Zero is reserved for
InvalidSubTransactionId.)  Note that subtransactions do not have their
own VXIDs; they use the parent top transaction's VXID.
在内部实现上,不论子事务是否拥有XIDs,后台进程需要标识子事务的方法;只要父顶级事务存在这种需求就好一直存在.
因此,产生了SubTransactionId,该字段类似于CommandId,在每次顶层事务都会重置的计数器.
顶层事务本身的SubTransactionId设定为1,其他子事务的ID为2或更大(0保留用于InvalidSubTransactionId).
注意子事务没有VXIDs;它们使用顶层事务的VXID.

TransactionState结构体


typedef enum TransState
{
    TRANS_DEFAULT,                
    TRANS_START,                
    TRANS_INPROGRESS,            
    TRANS_COMMIT,                
    TRANS_ABORT,                
    TRANS_PREPARE                
} TransState;

typedef enum TBlockState
{
    
    TBLOCK_DEFAULT,                
    TBLOCK_STARTED,                
    
    TBLOCK_BEGIN,                
    TBLOCK_INPROGRESS,            
    TBLOCK_IMPLICIT_INPROGRESS, 
    TBLOCK_PARALLEL_INPROGRESS, 
    TBLOCK_END,                    
    TBLOCK_ABORT,                
    TBLOCK_ABORT_END,            
    TBLOCK_ABORT_PENDING,        
    TBLOCK_PREPARE,                
    
    TBLOCK_SUBBEGIN,            
    TBLOCK_SUBINPROGRESS,        
    TBLOCK_SUBRELEASE,            
    TBLOCK_SUBCOMMIT,            
    TBLOCK_SUBABORT,            
    TBLOCK_SUBABORT_END,        
    TBLOCK_SUBABORT_PENDING,    
    TBLOCK_SUBRESTART,            
    TBLOCK_SUBABORT_RESTART        
} TBlockState;

typedef struct TransactionStateData
{
    FullTransactionId fullTransactionId;    
    SubTransactionId subTransactionId;    
    char       *name;            
    int            savepointLevel; 
    TransState    state;            
    TBlockState blockState;        
    int            nestingLevel;    
    int            gucNestLevel;    
    MemoryContext curTransactionContext;    
    ResourceOwner curTransactionOwner;    
    TransactionId *childXids;    
    int            nChildXids;        
    int            maxChildXids;    
    Oid            prevUser;        
    int            prevSecContext; 
    bool        prevXactReadOnly;    
    bool        startedInRecovery;    
    bool        didLogXid;        
    int            parallelModeLevel;    
    bool        chain;            
    struct TransactionStateData *parent;    
} TransactionStateData;
typedef TransactionStateData *TransactionState;

到此,相信大家对“怎么理解PostgreSQL事务管理中的子事务”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯