当前位置:网站首页>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
边栏推荐
- Redis(二)—Redis通用命令
- Three updates to build applications for different types of devices | 2022 i/o key review
- Etcd database source code analysis -- starting from the start function of raftnode
- Change the original style of UI components
- c语言面试写一个函数在字符串N中查找第一次出现子串M的位置。
- Wechat applet hides the progress bar component of the video tag
- 693. Travel sequencing
- Find duplicate email addresses
- 微信小程序隐藏video标签的进度条组件
- 字符串常量与字符串对象分配内存时的区别
猜你喜欢

Rk3399 platform development series explanation (WiFi) 5.52. Introduction to WiFi framework composition

2022 Android interview essential knowledge points, a comprehensive summary

POI导出Excel:设置字体、颜色、行高自适应、列宽自适应、锁住单元格、合并单元格...

Force deduction 62 different paths (the number of all paths from the upper left to the lower right of the matrix) (dynamic planning)

Open the blue screen after VMware installation

面试中有哪些经典的数据库问题?

Markdown displays pictures side by side
![[FPGA] EEPROM based on I2C](/img/28/f4f2efda4b5feb973c9cf07d9d908f.jpg)
[FPGA] EEPROM based on I2C

Overview of FlexRay communication protocol

3531. Huffman tree
随机推荐
How to solve sqlstate[hy000]: General error: 1364 field 'xxxxx' doesn't have a default value error
Leite smart home longhaiqi: from professional dimming to full house intelligence, 20 years of focus on professional achievements
"Parse" focalloss to solve the problem of data imbalance
MySQL的安装
C language (structure) defines a user structure with the following fields:
Database notes 04
Basic DOS commands
Experience sharing of contribution of "management world"
【GNN】图解GNN: A gentle introduction(含视频)
Calculation model FPS
【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽
Redisl garbled code and expiration time configuration
Party A's requirements for those who have lost 800 yuan
一段程序让你明白什么静态内部类,局部内部类,匿名内部类
计算模型 FPS
A program lets you understand what static inner classes, local inner classes, and anonymous inner classes are
Redis(一)——初识Redis
go-microservice-simple(2) go-Probuffer
Qt多线程的多种方法之一 QThread
Implementation of VGA protocol based on FPGA