当前位置:网站首页>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
边栏推荐
- 3531. Huffman tree
- Peripheral driver library development notes 43: GPIO simulation SPI driver
- JWT 认证
- 2022Android面试必备知识点,一文全面总结
- MySQL卸载文档-Windows版
- Swagger3 configuration
- FlexRay通信协议概述
- Niuke Xiaobai monthly race 52 E. sum logarithms in groups (two points & inclusion and exclusion)
- Doctoral application | Professor Hong Liang, Academy of natural sciences, Shanghai Jiaotong University, enrolls doctoral students in deep learning
- dolphinscheduler3.x本地启动
猜你喜欢
缓存在高并发场景下的常见问题
ICML 2022 | explore the best architecture and training method of language model
laravel 使用腾讯云 COS5全教程
Redis (I) -- getting to know redis for the first time
港科大&MSRA新研究:关于图像到图像转换,Fine-tuning is all you need
693. 行程排序
Redis (II) - redis General Command
MySQL卸载文档-Windows版
3531. 哈夫曼树
Redis(一)——初识Redis
随机推荐
Jmeter自带函数不够用?不如自己动手开发一个
【解决】Final app status- UNDEFINED, exitCode- 16
JWT 认证
2022 Android interview essential knowledge points, a comprehensive summary
Rk3399 platform development series explanation (interruption) 13.10, workqueue work queue
微信小程序隐藏video标签的进度条组件
You don't know the complete collection of recruitment slang of Internet companies
POI导出Excel:设置字体、颜色、行高自适应、列宽自适应、锁住单元格、合并单元格...
Redis (I) -- getting to know redis for the first time
C language (structure) defines a user structure with the following fields:
Apache ab 压力测试
Redisl garbled code and expiration time configuration
MySQL(十)
MySQL的安装
Haqi projection Black Horse posture, avec seulement six mois de forte pénétration du marché des projecteurs de 1000 yuans!
C语言面试 写一个函数查找两个字符串中的第一个公共字符串
如何解决数据库插入数据显示SQLSTATE[HY000]: General error: 1364 Field ‘xxxxx‘ doesn‘t have a default value错误
[FPGA] EEPROM based on I2C
Jstack of JVM command: print thread snapshots in JVM
Test the foundation of development, and teach you to prepare for a fully functional web platform environment