当前位置:网站首页>Postgresql源码(60)事务系统总结
Postgresql源码(60)事务系统总结
2022-07-07 02:05:00 【mingjie73】
相关
《Postgresql源码(23)Clog使用的Slru页面淘汰机制》
重新总结下PG的事务管理系统:
PG中的事务处理按提供的功能可以分为两大部分:基本事务状态管理、子事务状态管理。
PG的事务系统总结起来一句话:用户命令触发状态机函数导致事务状态流转,流转时按对应状态调用底层事务处理函数干活。
1 状态机流转系统
用户命令触发状态机函数导致事务状态流转,流转时按对应状态调用底层事务处理函数干活。
状态机流转函数
12个状态机流转函数,可以分成三类。
- 包裹所有单行SQL的两个函数(进入SQL前StartTransactionCommand、SQL执行后CommitTransactionCommand),无论你执行的是begin、还是select 1,都会走一遍这两个函数。相当于事务状态的被动流转。
b StartTransactionCommand
b CommitTransactionCommand
- 事务块处理函数,对应用户事务命令,在PortalRun里面调用,主动流转事务状态。
// 系统内部调用回滚
b AbortCurrentTransaction
// 用户执行begin
b BeginTransactionBlock
// 用户执行commit
b EndTransactionBlock
// 用户执行rollback、abort
b UserAbortTransactionBlock
- 子事务状态流转。
b DefineSavepoint
b ReleaseSavepoint
b RollbackToSavepoint
b BeginInternalSubTransaction
b RollbackAndReleaseCurrentSubTransaction
b AbortOutOfAnyTransaction
底层事务处理函数
状态机流转时会调用底层函数干活,函数分两类
- 基础事务功能
// 启动事务时调用,配置事务状态,申请资源等
StartTransaction
// 事务正常提交是调用
CommitTransaction
// 事务回滚时调用,先调AbortTransaction,在调CleanupTransaction
CleanupTransaction
// 事务回滚时调用
AbortTransaction
- 子事务功能
StartSubTransaction
CommitSubTransaction
AbortSubTransaction
CleanupSubTransaction
2 底层事务处理函数做了什么?
StartTransaction
- 拿到vxid(由backendid和localid组合值)表示虚拟的事务id(写还未发生,不能分配真实xid)
- 用vxid注册MyProc,注册后在锁表中可以查询到vxid锁,表示事务启动了
- 事务状态流转到TRANS_INPROGRESS
StartTransaction
// vxid = {backendId = 3, localTransactionId = 76407}
GetNextLocalTransactionId
VirtualXactLockTableInsert
...
s->state = TRANS_INPROGRESS;
...
CommitTransaction(事务内有写操作)
已分配事务ID的场景
- 事务状态流转TRANS_COMMIT
- 开启对于checkpoint的临界区:MyProc->delayChkpt = true(《Postgresql源码(27)为什么事务提交会通过delayChkpt阻塞checkpoint》)
- 写commit的事务日志、刷事务日志
- 写clog(不刷)TransactionIdCommitTree
- 清理ProcArray中的xid信息
- 清理其他
- 事务状态流转TRANS_DEFAULT
CommitTransaction
s->state = TRANS_COMMIT
RecordTransactionCommit
[START_CRIT_SECTION]
[[MyProc->delayChkpt = true]]
XactLogCommitRecord
XLogFlush
TransactionIdCommitTree
[[MyProc->delayChkpt = false]]
[END_CRIT_SECTION]
ProcArrayEndTransaction
// 能拿到锁正常清理,拿不到锁加到list里面等着后面清理
LWLockConditionalAcquire(ProcArrayLock, LW_EXCLUSIVE)
// 正常清理:清理MyProc和ProcGlobal里面记录的xid信息
ProcArrayEndTransactionInternal
...
// 清理
...
s->state = TRANS_DEFAULT
CommitTransaction(事务内无写操作)
未分配事务ID
- 事务状态流转TRANS_COMMIT
- 清理
- 事务状态流转TRANS_DEFAULT
CommitTransaction
s->state = TRANS_COMMIT
RecordTransactionCommit // do nothing
// 清理
s->state = TRANS_DEFAULT
3 事务ID分配
在真正要写数据前,会调用GetCurrentTransactionId,比如heap_insert。
- 拿一个新的xid
- 配置到MyProc->xid
- 配置到ProcGlobal->xids[MyProc->pgxactoff]
- xid加锁XactLockTableInsert
TransactionId
GetCurrentTransactionId(void)
{
TransactionState s = CurrentTransactionState;
if (!FullTransactionIdIsValid(s->fullTransactionId))
AssignTransactionId(s);
return XidFromFullTransactionId(s->fullTransactionId);
}
如果没分配过,执行AssignTransactionId拿一个新的xid分配给TransactionState。参考这一篇(《Postgresql源码(59)事务ID取值和判断规律总结》)
AssignTransactionId
...
GetNewTransactionId
...
MyProc->xid = xid;
ProcGlobal->xids[MyProc->pgxactoff] = xid;
...
XactLockTableInsert
...
4 子事务系统
例子:
drop table t1;
create table t1 (c1 int, c2 int);
begin;
insert into t1 values (1,1);
savepoint a;
insert into t1 values (2,1);
savepoint b;
insert into t1 values (3,1);
下面记录一些和普通事务的差异点:
事务状态:子事务会使CurrentTransactionState有多层结构,之间使用parent连接。
p *CurrentTransactionState
$39 = {fullTransactionId = {value = 4000071}, subTransactionId = 3, name = 0x199ca60 "b", savepointLevel = 0,
state = TRANS_INPROGRESS, blockState = TBLOCK_SUBINPROGRESS, nestingLevel = 3, gucNestLevel = 3,
curTransactionContext = 0x19de090, curTransactionOwner = 0x192a458, childXids = 0x0, nChildXids = 0, maxChildXids = 0,
prevUser = 10, prevSecContext = 0, prevXactReadOnly = false, startedInRecovery = false, didLogXid = false,
parallelModeLevel = 0, chain = false, assigned = false, parent = 0x199c558}
p *CurrentTransactionState->parent
$40 = {fullTransactionId = {value = 4000070}, subTransactionId = 2, name = 0x199c6c0 "a", savepointLevel = 0,
state = TRANS_INPROGRESS, blockState = TBLOCK_SUBINPROGRESS, nestingLevel = 2, gucNestLevel = 2,
curTransactionContext = 0x19d7200, curTransactionOwner = 0x19164c8, childXids = 0x0, nChildXids = 0, maxChildXids = 0,
prevUser = 10, prevSecContext = 0, prevXactReadOnly = false, startedInRecovery = false, didLogXid = true, parallelModeLevel = 0,
chain = false, assigned = false, parent = 0xe6a940 <TopTransactionStateData>}
p *CurrentTransactionState->parent->parent
$41 = {fullTransactionId = {value = 4000069}, subTransactionId = 1, name = 0x0, savepointLevel = 0, state = TRANS_INPROGRESS,
blockState = TBLOCK_INPROGRESS, nestingLevel = 1, gucNestLevel = 1, curTransactionContext = 0x199c400,
curTransactionOwner = 0x191e3e8, childXids = 0x0, nChildXids = 0, maxChildXids = 0, prevUser = 10, prevSecContext = 0,
prevXactReadOnly = false, startedInRecovery = false, didLogXid = true, parallelModeLevel = 0, chain = false, assigned = false,
parent = 0x0}
在分配事务ID时,每个子事务都会拿到自己的XID。
AssignTransactionId
...
SubTransSetParent(XidFromFullTransactionId(s->fullTransactionId),
XidFromFullTransactionId(s->parent->fullTransactionId));
并把自己上一层的xid记录到subtrans中(SLRU页面和CLOG使用的是相同的机制,这篇讲了一部分《Postgresql源码(23)Clog使用的Slru页面淘汰机制》)。
子事务提交
- 除了上文(CommitTransaction(事务内有写操作))提到的步骤
- 在写CLOG时,和无子事务的情况有所区别
- 如果没有子事务,直接写CLOG即可
- 存在子事务时
- 在写CLOG时首先把子事务的XID配置SUB_COMMITTED状态到CLOG中
- 然后把父XID的提交状态配置进去
- 然后再把子事务的XID的状态从SUB_COMMITTED变成提交状态(类似于两阶段提交)
CommitTransaction
RecordTransactionCommit
TransactionIdCommitTree
TransactionIdSetTreeStatus
// 一个CLOG页面全部搞定
TransactionIdSetPageStatus
TransactionIdSetPageStatusInternal
// 每一个subxid都配置TRANSACTION_STATUS_SUB_COMMITTED
for
TransactionIdSetStatusBit
// 配置主事务的
TransactionIdSetStatusBit
// 再配置子事务的提交
for
TransactionIdSetStatusBit
边栏推荐
- JVM 全面深入
- Audio distortion analysis of DSP and DAC based on adau1452
- Haqi projection Black Horse posture, avec seulement six mois de forte pénétration du marché des projecteurs de 1000 yuans!
- 地质学类比较有名的外文期刊有哪些?
- Redis (II) - redis General Command
- VMware安装后打开就蓝屏
- 软件测试到了35岁,真的就干不动了吗?
- Array proof during st table preprocessing
- Open the blue screen after VMware installation
- 开发者别错过!飞桨黑客马拉松第三期链桨赛道报名开启
猜你喜欢
博士申请 | 上海交通大学自然科学研究院洪亮教授招收深度学习方向博士生
uniapp开发小程序如何使用微信云托管或云函数进行云开发
What are the classic database questions in the interview?
基于ADAU1452的DSP及DAC音频失真分析
雷特智能家居龙海祁:从专业调光到全宅智能,20年专注成就专业
How to use wechat cloud hosting or cloud functions for cloud development of unapp development applet
Redis (II) - redis General Command
哈趣投影黑马之姿,仅用半年强势突围千元投影仪市场!
Software testing knowledge reserve: how much do you know about the basic knowledge of "login security"?
Overview of FlexRay communication protocol
随机推荐
【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽
UIC (configuration UI Engineering) public file library adds 7 industry materials
Symmetric binary tree [tree traversal]
雷特智能家居龙海祁:从专业调光到全宅智能,20年专注成就专业
Test the foundation of development, and teach you to prepare for a fully functional web platform environment
ICML 2022 | explore the best architecture and training method of language model
[opencv] morphological filtering (2): open operation, morphological gradient, top hat, black hat
Swagger3 configuration
"Parse" focalloss to solve the problem of data imbalance
Calculation model FPS
MySQL的安装
A program lets you understand what static inner classes, local inner classes, and anonymous inner classes are
Redis(二)—Redis通用命令
Handling hardfault in RT thread
K8s running Oracle
Apache ab 压力测试
Leite smart home longhaiqi: from professional dimming to full house intelligence, 20 years of focus on professional achievements
生活中的开销,怎么记账合适
Party A's requirements for those who have lost 800 yuan
Wechat applet hides the progress bar component of the video tag