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…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
