Postgresql源码(123)事务提交时三段资源释放分析ResourceOwnerRelease
0 总结
- 三段释放原因:因为如果先释放锁,没有释放一些共享资源(比如pin住的buffer),别人拿到锁后发现我们仍然持有一些资源,就会有问题。所以三阶段释放主要是以锁为分界线,先释放锁保护的资源,在释放锁,在清理私有资源。这样可以保证别人拿到锁后,一定也能拿到对应的资源。
- 三段:先放pinned buffer、relation、dsm这些共享资源;再放锁;所有放其他会话看不到的私有资源。
1 资源随事务释放
三阶段释放是指ResourceOwnerRelease函数在使用时需要调用三次,按固定顺序调用每次删除特定的资源:
- RESOURCE_RELEASE_BEFORE_LOCKS
- RESOURCE_RELEASE_LOCKS
- RESOURCE_RELEASE_AFTER_LOCKS
typedef enum
{RESOURCE_RELEASE_BEFORE_LOCKS = 1,RESOURCE_RELEASE_LOCKS,RESOURCE_RELEASE_AFTER_LOCKS,
} ResourceReleasePhase;
例如事务提交时CommitTransaction:
static void
CommitTransaction(void).........ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_BEFORE_LOCKS,true, true);/* Check we've released all buffer pins */AtEOXact_Buffers(true);/* Clean up the relation cache */AtEOXact_RelationCache(true);/** Make catalog changes visible to all backends. This has to happen after* relcache references are dropped (see comments for* AtEOXact_RelationCache), but before locks are released (if anyone is* waiting for lock on a relation we've modified, we want them to know* about the catalog change before they start using the relation).*/AtEOXact_Inval(true);AtEOXact_MultiXact();ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_LOCKS,true, true);ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_AFTER_LOCKS,true, true);......
其他几个事务控制函数资源释放时,也是按照相同的顺序分三阶段释放的:
函数名 | phase | isCommit | isTopLevel |
---|---|---|---|
CommitTransaction | RESOURCE_RELEASE_BEFORE_LOCKS | true | true |
CommitTransaction | RESOURCE_RELEASE_LOCKS | true | true |
CommitTransaction | RESOURCE_RELEASE_AFTER_LOCKS | true | true |
PrepareTransaction | RESOURCE_RELEASE_BEFORE_LOCKS | true | true |
PrepareTransaction | RESOURCE_RELEASE_LOCKS | true | true |
PrepareTransaction | RESOURCE_RELEASE_AFTER_LOCKS | true | true |
AbortTransaction | RESOURCE_RELEASE_BEFORE_LOCKS | false | true |
AbortTransaction | RESOURCE_RELEASE_LOCKS | false | true |
AbortTransaction | RESOURCE_RELEASE_AFTER_LOCKS | false | true |
CommitSubTransaction | RESOURCE_RELEASE_BEFORE_LOCKS | true | false |
CommitSubTransaction | RESOURCE_RELEASE_LOCKS | true | false |
CommitSubTransaction | RESOURCE_RELEASE_AFTER_LOCKS | true | false |
AbortSubTransaction | RESOURCE_RELEASE_BEFORE_LOCKS | false | false |
AbortSubTransaction | RESOURCE_RELEASE_LOCKS | false | false |
AbortSubTransaction | RESOURCE_RELEASE_AFTER_LOCKS | false | false |
2 为什么要分三阶段释放?
结论
因为如果先释放锁,没有释放一些共享资源(比如pin住的buffer),别人拿到锁后发现我们仍然持有一些资源,就会有问题。所以三阶段释放主要是以锁为分界线,先释放锁保护的资源,在释放锁,在清理私有资源。这样可以保证别人拿到锁后,一定也能拿到对应的资源。
- RESOURCE_RELEASE_BEFORE_LOCKS(预锁定阶段):需要释放对其他后端可见的资源(pinned buffers)。为了确保当我们释放另一个后端可能正在等待的锁时,它会看到我们已经完全退出了我们的事务。这是为了防止在释放锁之后,其他后端仍然看到我们持有的资源,从而可能导致数据不一致或其他问题。
- RESOURCE_RELEASE_LOCKS(锁定阶段):释放持有的所有锁。这是为了让其他可能正在等待这些锁的后端能够继续执行。
- RESOURCE_RELEASE_AFTER_LOCKS(后锁定阶段):后端内部的清理工作。释放那些只有后端自己知道的、不会影响其他后端的资源。
3 代码分析
下面这次提交后对resowner做了扩展性增强,代码逻辑没变但可读性有点差(PG17dev分支)
commit b8bff07daa85c837a2747b4d35cd5a27e73fb7b2
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed Nov 8 13:30:50 2023 +0200Make ResourceOwners more easily extensible.
围绕本篇主题,后面分析这次提交前代码(PG17前的逻辑)
static void
ResourceOwnerReleaseInternal(ResourceOwner owner,ResourceReleasePhase phase,bool isCommit,bool isTopLevel)
{ResourceOwner child;ResourceOwner save;ResourceReleaseCallbackItem *item;ResourceReleaseCallbackItem *next;Datum foundres;
resowner维护树形结构,递归释放自己的child资源。
for (child = owner->firstchild; child != NULL; child = child->nextchild)ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);...
3.1 第一阶段:RESOURCE_RELEASE_BEFORE_LOCKS
- 事务提交时不应该有正在进行中的io了,这里需要把标志位清理干净,避免影响后面事务使用这个buffer。
- 清理pinned的buffer
- 提交时不应该有pinned的了,会告警,回滚时就无所谓了,因为错误不一定出在哪,不一定给机会清理。
- 关闭打开的relation
- 提交时不应该有打开的了,会告警;回滚时同上。
- 关闭dsm段
- 关闭jit context
if (phase == RESOURCE_RELEASE_BEFORE_LOCKS){while (ResourceArrayGetAny(&(owner->bufferioarr), &foundres)){Buffer res = DatumGetBuffer(foundres);if (isCommit)elog(PANIC, "lost track of buffer IO on buffer %d", res);AbortBufferIO(res);}while (ResourceArrayGetAny(&(owner->bufferarr), &foundres)){Buffer res = DatumGetBuffer(foundres);if (isCommit)PrintBufferLeakWarning(res);ReleaseBuffer(res);}while (ResourceArrayGetAny(&(owner->relrefarr), &foundres)){Relation res = (Relation) DatumGetPointer(foundres);if (isCommit)PrintRelCacheLeakWarning(res);RelationClose(res);}while (ResourceArrayGetAny(&(owner->dsmarr), &foundres)){dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres);if (isCommit)PrintDSMLeakWarning(res);dsm_detach(res);}while (ResourceArrayGetAny(&(owner->jitarr), &foundres)){JitContext *context = (JitContext *) DatumGetPointer(foundres);jit_release_context(context);}......}}
3.2 第二阶段:RESOURCE_RELEASE_LOCKS
- 如果是顶层事务,直接释放所有锁,具体:
- 提交时要保留会话锁,释放事务锁。事务没了会话锁还需要继续生效,生命周期比事务长。
- 回滚时要释放所有锁。
- 会话锁:咨询锁。
- 事务锁:行锁、表锁等。
- 如果是子事务,按提交回滚做出不同行为。
- 提交:转移锁到parent resowner。
- 回滚:释放锁。
else if (phase == RESOURCE_RELEASE_LOCKS){if (isTopLevel){if (owner == TopTransactionResourceOwner){ProcReleaseLocks(isCommit);ReleasePredicateLocks(isCommit, false);}}else{LOCALLOCK **locks;int nlocks;...if (isCommit)LockReassignCurrentOwner(locks, nlocks);elseLockReleaseCurrentOwner(locks, nlocks);}}
3.3 第三阶段:RESOURCE_RELEASE_AFTER_LOCKS
- 释放catcache系统表缓存。
- 释放cached plan。
- 释放tuple desc,注意tuple desc在构造好后,会用IncrTupleDescRefCount函数,在resowner中记录,按引用计数控制删除。引用计数的机制主要是为了处理TupleDesc在多个地方共享使用的情况。例如一个查询的多个部分可能都需要引用同一个TupleDesc。如果在一个部分结束时就直接删除TupleDesc,那么其他部分就无法继续使用这个TupleDesc了。
- 释放快照。
- 释放fd。
else if (phase == RESOURCE_RELEASE_AFTER_LOCKS){while (ResourceArrayGetAny(&(owner->catrefarr), &foundres)){HeapTuple res = (HeapTuple) DatumGetPointer(foundres);if (isCommit)PrintCatCacheLeakWarning(res);ReleaseCatCache(res);}while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres)){CatCList *res = (CatCList *) DatumGetPointer(foundres);if (isCommit)PrintCatCacheListLeakWarning(res);ReleaseCatCacheList(res);}while (ResourceArrayGetAny(&(owner->planrefarr), &foundres)){CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);if (isCommit)PrintPlanCacheLeakWarning(res);ReleaseCachedPlan(res, owner);}while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres)){TupleDesc res = (TupleDesc) DatumGetPointer(foundres);if (isCommit)PrintTupleDescLeakWarning(res);DecrTupleDescRefCount(res);}while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres)){Snapshot res = (Snapshot) DatumGetPointer(foundres);if (isCommit)PrintSnapshotLeakWarning(res);UnregisterSnapshot(res);}/* Ditto for temporary files */while (ResourceArrayGetAny(&(owner->filearr), &foundres)){File res = DatumGetFile(foundres);if (isCommit)PrintFileLeakWarning(res);FileClose(res);}}for (item = ResourceRelease_callbacks; item; item = next){next = item->next;item->callback(phase, isCommit, isTopLevel, item->arg);}CurrentResourceOwner = save;
}
相关文章:

Postgresql源码(123)事务提交时三段资源释放分析ResourceOwnerRelease
0 总结 三段释放原因:因为如果先释放锁,没有释放一些共享资源(比如pin住的buffer),别人拿到锁后发现我们仍然持有一些资源,就会有问题。所以三阶段释放主要是以锁为分界线,先释放锁保护的资源&…...

电脑文件误删除如何恢复?2024最新三种恢复方法
我们在使用电脑的过程中,随着时间的不断推移,渐渐的我们会发现C盘内存空间不足了。这是因为我们很多文件都默认存储在C盘,所以导致C盘空间不足,电脑运行越来越慢。那么电脑哪些文件可以删除,电脑删除的东西怎么恢复&am…...

Netty应用——Google Protobuf强化篇(二十)
Protobuf发送一种实例 客户端可以发送一个 Student PoJo 对象到服务器 (通过 Protobuf 编码)服务端能接收 Student PoJo 对象,并显示信息(通过 Protobuf 解码) Student.proto syntax "proto3"; //版本 option java_outer_classname "StudentPOJO&…...

SpringAMQP开启“可靠性”机制
前言 上一篇介绍了如何在 《SpringBoot 中集成和使用消息队列》,看过这一篇就基本上可以在SpringBoot中使用消息队列了,但是消息队列他归根结底是一个客户端服务器模式的中间件,面对复杂的网络环境和分布式使用环境,难免会出现各…...

戴尔Dell R740服务器开机冒烟亮黄灯故障维修
今天分享的是一台过保修期的DELL PowerEdge R740服务器开机冒烟的维修案例。先上图: 接到用户报修后工程师立即响应,由于用户也是刚开工第一天服务器开机就出现了这种祥龙吐雾的祥兆,导致工厂业务流程无法正常使用,这台机器在东莞…...

【阅读笔记】空域保边降噪《Side Window Filtering》
1、保边滤波背景 保边滤波器的代表包括双边滤波、引导滤波,但是这类滤波器有一个问题,它们均将待处理的像素点放在了方形滤波窗口的中心。但如果待处理的像素位于图像纹理或者边缘,方形滤波核卷积的处理结果会导致这个边缘变模糊。 基于这个…...

vue3前端excel导出;组件表格,自定义表格导出;Vue3 + xlsx + xlsx-style
当画面有自定义的表格或者样式过于复杂的表格时,导出功能可以由前端实现 1. 使用的插件 : sheet.js-xlsx 文档地址:https://docs.sheetjs.com/ 中文地址:https://geekdaxue.co/read/SheetJS-docs-zh/README.md xlsx-style&#…...

npm install一直卡在 sill idealTree buildDeps
当npm install命令在安装过程中卡在sill idealTree buildDeps阶段时,可能的原因包括网络延迟、镜像源问题或缓存问题。以下是一些可能的解决方法: 检查镜像源: 打开命令提示符(cmd)窗口。 输入命令npm config get…...

spring boot rabbitmq常用配置
直接上代码 package com.example.demo;import org.aopalliance.aop.Advice; import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframewo…...

MySQL学习记录——십삼 视图及用户、权限管理
文章目录 1、视图2、用户管理3、权限管理 1、视图 视图把查询出来的结果以表结构的形式存储起来,视图和基表有关系,两者的数据变化都会互相影响。 在查询时,假如要经常查询一条记录,select …,那么为了方便ÿ…...

PyCharm 自动添加文件头注释
PyCharm 自动添加文件头注释 1. File and Code Templates2. Python FileReferences 1. File and Code Templates File -> Settings -> Editor -> File and Code Templates -> Python Script Reformat according to style & Enable Live Templates Created by…...

用HTML Canvas和JavaScript创建美丽的花朵动画效果
目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>炫酷花朵</title><style>* {margin: 0;padding: 0;overflow: hidden;bac…...

java----js常用的api
java----js常用的api 时间函数获取当前时间: DateUtil.today()时间偏移字符换时间格式化 map.computeIfAbsent添加list 时间函数 获取当前时间: DateUtil.today() String todayDateUtil.today()String today “2024-02-01”; 时间偏移 往前退役30天 DateUtil.offsetDay(D…...

unity 使用VS Code 开发,VS Code配置注意事项
vscode 对应的插件(unity开发) 插件:.Net Install Tool,c#,c# Dev Kit,IntelliCode For C# Dev Kit,Unity,Unity Code Snippets 本人现在是用了这些插件 unity需要安装Visual Studio Editor 1、.Net Install Tool 设置 需要在设置里面配置…...

领域驱动设计(Domain Driven Design)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、场景和要求二、领域模型关键词1.领域2.子域3.通用语言4.限界上下文5.领域模型6.实体和值对象7.聚合根8.领域服务9.领域事件 总结 前言 Domain Driven Desi…...

CF778A String Game 题解
文章目录 CF778A String Game 题解题面翻译Input DataOutput DataInput Sample 1Output Sample 1题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示算法:二分代码: CF778A String Game 题解 link 题面翻译 …...

【工具插件类教学】Unity运行时监控变量,属性,事件等的值和调用Runtime Monitoring
目录 一、介绍 二、安装方式 三、入门 1.实例化和静态成员...

实际生产中的一次非典型的基于jmeter的接口自动化实践
实际工作中遇到过一次自动化巡检的需求,作为测试人员没法从源代码入手,加之数据库也不熟悉,故采取接口自动化的方式来实现巡检,算是一种歪门邪道吧,应该不是接口自动化的常规使用方式。jmeter在这里的作用实际上也只是…...

新能源汽车软件开发设计规范
新能源汽车 软件开发设计规范 版本: 1.0 编 制: 校 对: 审 核: 会 签: …...

Linux:grep进阶(11)
Linux:shell脚本:基础使用(4)《正则表达式-grep工具》_shell grep 全角字符串-CSDN博客https://blog.csdn.net/w14768855/article/details/132338954?ops_request_misc%257B%2522request%255Fid%2522%253A%252217083360171680022…...

【实战】二、Jest难点进阶(一) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(五)
文章目录 一、Jest 前端自动化测试框架基础入门二、Jest难点进阶1.snapshot 快照测试 学习内容来源:Jest入门到TDD/BDD双实战_前端要学的测试课 相对原教程,我在学习开始时(2023.08)采用的是当前最新版本: 项版本babe…...

8.2 新特性 - 透明的读写分离
文章目录 前言1. 安装部署1.1 下载安装包1.2 MySQL Shell1.3 配置 MySQL 实例1.4 启动 ReplicaSet1.5 启动 8.2 Router 2. 测试路由总结 前言 MySQL 8.0 官方推出过一个高可用方案 ReplicaSet 主要由 Router、MySQL Shell、MySQL Server 三个组件组成。 MySQL Shell 负责管理…...

关于三维GIS开发成长路线的一些思考
三维GIS是将GIS三维化表达,从一个三维GIS开发门外汉的角度来看,三维GIS开发成长路线分几个层面: 第一层面 做三维开发,最基本的Cesium、ThreeJS、MapBox这些要能做到接口级熟悉,熟悉接口是用来干嘛的,接口…...

git操作---> 使用git push,和使用git push origin HEAD:[分支名]有什么区别呢?
git push origin HEAD:branch2: 这个命令显式地指定了你要推送的本地引用(HEAD),以及远程仓库的目标引用(origin/branch2)。 HEAD 是一个引用,指向你当前所在的本地分支的最新提交。 这个命令的意图是将当…...

基于Java的大学社团管理平台
功能介绍 平台采用B/S结构,后端采用主流的Springboot框架进行开发,前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括:首页、社团详情、申请加入、用户中心模块。后台功能包括:社团管理、分类管理…...

1.函数模板基础
1.1函数模板作用: 建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表,提高复用性 1.2语法: //第一种 template <typename T> 函数声明或定义//第二种 template <class T&…...

22-k8s中pod的调度-亲和性affinity
一、概述 在k8s当中,“亲和性”分为三种,节点亲和性、pod亲和性、pod反亲和性; 亲和性分类名称解释说明nodeAffinity节点亲和性通过【节点】标签匹配,用于控制pod调度到哪些node节点上,以及不能调度到哪些node节点上&…...

通俗易懂,Spring Bean生命周期管理的理解
目录 1、实例化阶段 2、初始化阶段 3、销毁阶段 总结 在Spring框架中,Bean是最基本的组件,它是Spring框架中的一个Java对象。 下面通过Bean来理解bean的生命周期: Bean(initMethod "customInit", destroyMethod "cust…...

找座位 - 华为OD统一考试(C卷)
OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 在一个大型体育场内举办了一场大型活动,由于疫情防控的需要,要求每位观众的必须间隔至少一个空位才允许落座。 现在给出一排观众座位分布图,座位中存在已落座的观众,请计算出,在不移动现有观众座位…...

npm run dev运行出现NODE_OPTIONS=--max_old_space_size=4096 vite --mode dev --host?
问题描述 PS E:\AWorkDataease\DataEase\core\core-frontend> npm run dev dataease0.0.0 dev NODE_OPTIONS–max_old_space_size4096 vite --mode dev --host 0.0.0.0 ‘NODE_OPTIONS’ 不是内部或外部命令,也不是可运行的程序 或批处理文件。 解决方案 遇到…...