当前位置:网站首页>Postgresql source code (65) analysis of the working principle of the new snapshot system Globalvis
Postgresql source code (65) analysis of the working principle of the new snapshot system Globalvis
2022-08-03 19:03:00 【mingjie】
相关: 《Postgresql源码(18)PGPROC相关结构》《Postgresql源码(65)新快照体系Globalvis工作原理分析》《Postgresql快照优化Globalvis新体系分析(性能大幅增强)》《Improving Postgres Connection Scalability: Snapshots》
1 优化后的allProcs数组
先介绍下新版本PG删除了PGXACT结构后,事务ID是怎么存的:
- 之前的PGPROC和PGXACT结构是一一对应的,每个后端一个PGPROC、一个PGXACT,事务ID记录在PGXACT中,问题就像上一篇提的,PGXACT不是紧凑的,遍历起来无法高效利用cache.
- 新版本中,在PROC_HDR中原来的allPgXact不见了(指向PGXACT大数组),取而代之的是xids紧凑数组,注意!这个数组和pgprocnos索引是对应的.
- 所以找一个PROC的xid,除了直接从PGPROC中读,还可以从PROC_HDR->xids读取,这个数组是紧凑的,遍历起来效率更高.
debug命令备忘(假设numProcs=3) p *procArray p allProcs[procArray->pgprocnos[0]] p allProcs[procArray->pgprocnos[1]] p allProcs[procArray->pgprocnos[2]] p ProcGlobal->xids[0] p ProcGlobal->xids[1] p ProcGlobal->xids[2]
2 GetSnapshotData第一部分——循环拿活跃事务
总结:
- 遍历
ProcGlobal->xids
数组,拿到所有的xid(优化一:紧凑数组). - 活跃的xid添加到快照的xip数组中.
- 顺便算一个xmin(函数栈变量)记录最小的活跃事务id.
读取PGXACT->xmin(优化二)- xmax在进入循环前就算好了,一定是latestCompletedXid+1.
- 最后如果MyProc的xmin是无效的,更新一次
PGPROC->xmin
(还是要更新自己的xmin,这是必须的,无优化) - 整个过程包在ProcArrayLock大锁中.
代码流程:
GetSnapshotData
...
LWLockAcquire(ProcArrayLock, LW_SHARED)
...
for (int pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
/* 如果xid>=xmax可以直接跳过,因为这个xid是在我拿快照后启动的,对我来说肯定不可见 */
if (!NormalTransactionIdPrecedes(xid, xmax))
continue;
/* 如果xid < xmin,xid也要包含在xmin中,xmin表示最小的活跃事务 */
if (NormalTransactionIdPrecedes(xid, xmin))
xmin = xid;
/* 记录到快照中 */
xip[count++] = xid;
/* 更新一次xmin */
if (!TransactionIdIsValid(MyProc->xmin))
MyProc->xmin = TransactionXmin = xmin;
LWLockRelease(ProcArrayLock);
3 GetSnapshotData第二部分——维护GlobalVis
这部分要对比历史代码来看.
PG10
- 【0】前面把快照已经计算完了.下面“顺便”计算下全局最小可清理位点:RecentGlobalXmin.
- 【1】拿到最小事务ID.
- 【2】最小可清理位点传给RecentGlobalXmin,如果配了vacuum_defer_cleanup_age,把最小可清理位点,再往前推,清理时会保留更多的事务,给高延迟的备机使用.
- 【3】如果复制槽指定了某个位点,vacuum是不能清理的.
GetSnapshotData
//【0】前面把快照已经计算完了.下面“顺便”计算下全局最小可清理位点:RecentGlobalXmin
//【1】拿到最小事务ID
if (TransactionIdPrecedes(xmin, globalxmin))
globalxmin = xmin;
//【2】最小可清理位点传给RecentGlobalXmin,如果配了vacuum_defer_cleanup_age
// 把最小可清理位点,再往前推,清理时会保留更多的事务,给高延迟的备机使用
RecentGlobalXmin = globalxmin - vacuum_defer_cleanup_age;
if (!TransactionIdIsNormal(RecentGlobalXmin))
RecentGlobalXmin = FirstNormalTransactionId;
//【3】如果复制槽指定了某个位点,vacuum是不能清理的;
if (TransactionIdIsValid(replication_slot_xmin) &&
NormalTransactionIdPrecedes(replication_slot_xmin, RecentGlobalXmin))
RecentGlobalXmin = replication_slot_xmin;
...
...
PG14
测试场景:
事务一RC-----------4000440启动-------------------4000440结束------------------------------->
事务二RR----------------------4000450启动-----------------------------4000450结束---------->
事务三RC--------------------------------------------调试位置------------------------------->
调试位置:
GetSnapshotData
//【0】前面把快照已经计算完了.下面“顺便”计算下全局最小可清理位点,但是由于没有遍历PGPROC的xmin,只有自己遍历出来的xid,
/* maintain state for GlobalVis* */
{
TransactionId def_vis_xid;
TransactionId def_vis_xid_data;
FullTransactionId def_vis_fxid;
FullTransactionId def_vis_fxid_data;
FullTransactionId oldestfxid;
//【1】oldestfxid = 726
oldestfxid = FullXidRelativeTo(latest_completed, oldestxid);
//【2】def_vis_xid_data = 4000450
def_vis_xid_data =
TransactionIdRetreatedBy(xmin, vacuum_defer_cleanup_age);
//【3】def_vis_xid_data = 4000450
def_vis_xid_data =
TransactionIdOlder(def_vis_xid_data, replication_slot_xmin);
//【4】def_vis_xid = 4000450
def_vis_xid = def_vis_xid_data;
def_vis_xid = TransactionIdOlder(replication_slot_catalog_xmin, def_vis_xid);
def_vis_fxid = FullXidRelativeTo(latest_completed, def_vis_xid);
def_vis_fxid_data = FullXidRelativeTo(latest_completed, def_vis_xid_data);
//【5】4000440 ----> 4000450
GlobalVisSharedRels.definitely_needed =
FullTransactionIdNewer(def_vis_fxid,
GlobalVisSharedRels.definitely_needed);
...
//【6】4000440 ----> 4000440
GlobalVisSharedRels.maybe_needed =
FullTransactionIdNewer(GlobalVisSharedRels.maybe_needed,
oldestfxid);
...
}
总结:
- 【5】4000440 ----> 4000450:definitely_needed计算时实际上用的就是上面循环里面扫出来的最小的xid,在经过参数微调下(vacuumdelay和复制槽),definitely_needed的含义比较好理解,运行中的事务都是>=这个值的.
- 【6】4000440 ----> 4000440:maybe_needed取值是取:newer(自己的值、最小的冻结事务ID),这个值的含义是:小于这个值的事务应该都不活跃了.
不活跃 < maybe_needed < ... < definitely_needed <= 活跃
,那maybe_needed和definitely_needed中间的值怎么知道有没有提交?
看下面函数:
bool
GlobalVisTestIsRemovableFullXid(GlobalVisState *state,
FullTransactionId fxid)
{
// 比maybe_needed小的肯定已经不活跃了,可以清理!
if (FullTransactionIdPrecedes(fxid, state->maybe_needed))
return true;
// 比definitely_needed大于或等于,肯定活跃的,不能清理!
if (FullTransactionIdFollowsOrEquals(fxid, state->definitely_needed))
return false;
// 中间的怎么办?调用GlobalVisUpdate
if (GlobalVisTestShouldUpdate(state))
{
// 调用ComputeXidHorizons计算每一个PROC的xmin,然后更新到GlobalVis变量中.
GlobalVisUpdate();
Assert(FullTransactionIdPrecedes(fxid, state->definitely_needed));
return FullTransactionIdPrecedes(fxid, state->maybe_needed);
}
else
return false;
}
- (粗略判断)在判断具体一个XID能不能清理时,如果比definitely_needed大(或等),或比maybe_needed小,可以直接返回.
- (需要时精确判断)在判断maybe_needed和definitely_needed中间的xid时,要走优化前的老逻辑,遍历每个PGPROC的xmin,拿到全局最小的xmin,然后调用GlobalVisUpdateApply更新全局变量GlobalVisSharedRels.maybe_needed、GlobalVisSharedRels.definitely_needed的值,提供给后面函数使用.
4 (重要)优化后不在循环内读取PGXACT->xmin
会有什么影响?
- 循环PGPROC时,看不到别的进程的xmin了,例如,一个RR事务A在拿快照的时候,最小事务ID:10还没提交,那么这个RR快照的xmin就是10.
- 优化前,A事务后面新事务B构造快照时,能通过A的PGPROC看到xmin=10,并更新自己的xmin=10.
- 优化后,A事务后面新事务B构造快照时,只能看到自己遍历时最小的xid,例如这时10已经提交了,B看到的是15,那么后构造的快照就会认为全局最小xmin=15,但是由于A的xmin是10,所以这时全局清理位点需要是10,不能是15.
边栏推荐
猜你喜欢
微信小程序分享功能
BinaryIndexedTrees树状数组
【HCIP】MPLS实验
Mkke:为什么无法从Oracle 11g或12c升级到Oracle 23c?
Confused!Ali was abused on the one hand, but was fortunate to be promoted to Huawei's technology, and successfully got the offer, with an annual salary of 40w
如何理解即时通讯开发移动网络的“弱”和“慢”
【计网】二、物理层
87. (Home of cesium) cesium heat map (topography)
U-Net生物医学图像分割讲解(Convolutional Networks for BiomedicalImage Segmentation)
awk语法-02-运算、数组、格式化输出
随机推荐
sys文件系统
Alibaba senior experts create a learning architecture from scratch, including Alibaba's internal technology stack PPT, PFD actual combat
懵逼!阿里一面被虐了,幸获内推华为技术四面,成功拿到offer,年薪40w
技术开发人员常用的安全浏览器
Mkke:为什么无法从Oracle 11g或12c升级到Oracle 23c?
软件测试技术之如何编写测试用例(3)
Arduino实验三:继电器实验
Postgresql快照优化Globalvis新体系分析(性能大幅增强)
剑指Offer 56.数组中数字出现的次数
MPLS的简单应用于实验
MySQL——增删改查进阶
VsCode preview Geojson data
CC2530_ZigBee+华为云IOT:设计一套属于自己的冷链采集系统
C#爬虫之通过Selenium获取浏览器请求响应结果
idea——同一项目开启多个实例(不同端口)
OneNote 教程,如何在 OneNote 中设置页面格式?
fatal error: jni.h: No such file or directory
【C语言学习笔记(七)】C语言重定向输入与输出
go语言实现导出string字符串到文件中
Postgresql-xl全局快照与GTM代码走读(支线)