【PostgreSQL的CLOG解析】

news2024/11/27 10:32:55

image.png

同样还是这张图,之前发过shared_buffer和os cache、wal buffer和work mem的文章,今天的主题是图中的clog,即 commit log,PostgreSQL10之前放在数据库目录的pg_clog下面。PostgreSQL10之后修更名为xact,数据目录变更为pg_xact下面,表现形式是一些物理文件。

image.png


PostgreSQL为什么要使用clog呢,众所周知,PostgreSQL有着独特的MVCC机制,由于其多版本的特性,
在进行可见性判断时,需要获取事务的状态,即元组中 t_xmin 和 t_xmax 的状态,需要clog来记录事务的状态,从而判断其可见性,内存里的访问远远快于磁盘读写,因此PostgreSQL的很多机制都是运行时候在内存,然后定期持久化到磁盘。因此clog也有一块内存区域便于高效访问,即clog buffers,它也属于共享内存的这部分,平时更新clog是内存中进行的,然后满足条件后会调用pg_fsync刷数据到磁盘上的clog文件,或者等待checkpoint刷数据。数据库启动时会从磁盘的pg_xact目录下读取事务状态加载到clog buffers,并且运行过程中,vacuum会定时将不再使用的clog文件清理。

关于clog buffers 的大小,可以在 src/backend/access/transam/clog.c里看到相关定义。

image.png

所以clog buffers 占用的页的个数是NBuffers / 512,最大为128个页,最小为4个页,这里的NBuffers 在之前wal buffer这篇文章已经说过,它和shared_buffers的关系,两者计算的字节数是一致的,感兴趣可以去看下 (PostgreSQL的wal_buffers - 墨天轮)。
因此,这里clog buffers的大小可以理解为 shared_buffers的1/512。

PostgreSQL中通过clog来存储事务的状态。所以,当在Postgresql中如果想要取消一个执行了很长时间的事务,基本上是瞬间完成的,而不是像Oracle中一样需要等到undo表空间中内容回滚完,因为PostgreSQL里只需要将事务的状态由IN_PROGRESS修改为ABORTED即可。

PG中,事务号最多占用32位,有三个是比较特殊的,在access/xlogdefs.h下可以看到,这里的BootstrapTransactionId是用于“bootstrap”操作的XID,FrozenTransactionId用于非常老的元组。FirstNormalTransactionId是第一个“正常”的事务id。

image.png

一、事务状态

在clog.h里定义了需要提交日志clog来记录事务的状态,从而判断其可见性,在PostgreSQL里总共有四种事务状态。分别是:IN_PROGRESS、COMMITED、ABORTED和SUB_COMMITED。例如事务正在运行中,那么它的状态就是IN_PROGRESS。全部是0是初始状态,SUB_COMMITTED状态表示已提交的子事务,其父事务尚未提交或中止。每个状态只需要两位(2 bit)就可以表示。

image.png

二、clog文件里事务id和状态信息的空间占用

对于上述提到的四种状态,可以用2 bit来表示。因此四个事务的状态就占用了8 bit 即一个字节。
在src/backend/access/transam/clog.c里一样可以找到关于这块空间占用的定义。

image.png

CLOG_BITS_PER_XACT:每个事务占用几个 bit(默认为2,因为4种状态用2bit就可以完全表示)
CLOG_XACTS_PER_BYTE :每个字节可以存几个事务的状态(默认为4,因为1bytes=8bit,1个事务状态需要占用2bit)
CLOG_XACTS_PER_PAGE:每个页可以存几个事务的状态(8KB*4=32K=2^15)
CLOG_XACT_BITMASK:位掩码

三、如何根据事务ID查看在clog日志里的事务的状态

在PostgreSQL中,事务id并不是在事务开始时就会被真正分配,它会先分配一个虚拟事务号,当有数据要发生变化时才会真正分配xid,而当事务提交或回滚时,其事务状态便会被写入clog中。比如你显式开启事务,什么都不做或者只做查询操作,commit之后,是不会消耗xid的。而当你有对数据的变更操作,则会消耗xid。

举个例子如下,当我们执行 select txid_current();的时候,他每次也要使用一个事务号,而当我们显式开启事务,然后什么都不做或者只执行select操作后,commit以后,事务号是不会增加的。我测试中增加了1是因为执行了select txid_current();的原因。而当显示事务里有对数据的变更操作,则下次执行select txid_current();的时候,事务号直接跳了两个,减去一个select txid_current();的,剩下那个增加的事务号则是我这个insert的事务占用的。

postgres=# select txid_current();
 txid_current 
--------------
         2119
(1 row)

postgres=# select txid_current();
 txid_current 
--------------
         2120
(1 row)

postgres=# begin;
BEGIN
postgres=*# select 1;
 ?column? 
----------
        1
(1 row)
postgres=*# commit;
COMMIT
postgres=# select txid_current();
 txid_current 
--------------
         2121
(1 row)

postgres=# begin;
BEGIN
postgres=*# insert into t1 values(5);
INSERT 0 1
postgres=*# commit;
COMMIT
postgres=# select txid_current();
 txid_current 
--------------
         2123
(1 row)

在src/backend/access/transam/clog.c里同同样也存在着事务ID存放位置的定义和计算方法,如下所示

image.png

这四个分别为

TransactionIdToPage (事务id对应在哪个CLOG页)

计算方法为:(xid) / (TransactionId) CLOG_XACTS_PER_PAGE,这个CLOG_XACTS_PER_PAGE是第二部分看到的每个页可以存几个事务的状态,它默认是2^15。因此。事务id/ (2^15)得到的就是事务id对应在哪个CLOG页,当然,是要取整的。从0号页开始。

TransactionIdToPgIndex(事务id对应在上面页中的偏移量)

计算方法为:(xid) % (TransactionId) CLOG_XACTS_PER_PAGE,即事务id%(2^15)得到的是在页里的偏移量。

TransactionIdToByte(事务id对应在上面页中第几个的字节)

计算方法为:TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE,这里的TransactionIdToPgIndex(xid)是刚才计算的偏移量。而CLOG_XACTS_PER_BYTE是第二部分定义的每个字节可以存几个事务的状态,默认是4,所以事务在页里的偏移量/4得到的是事务id对应在页中第几个的字节。

TransactionIdToBIndex(事务id对应在上面字节中的哪个bit)

计算方法为:(xid) % (TransactionId) CLOG_XACTS_PER_BYTE。这里 CLOG_XACTS_PER_BYTE依旧是每个字节可以存几个事务的状态,默认为4,此处不用偏移量。直接用事务id%4来得到在一个byte里的哪个bit。(1byte=8bit)

这里做一个验证,
开启一个session

image.png

另开一个session,查看clog

image.png

计算四个值,我们该条记录是一个新的bytes里的
事务id对应在哪个CLOG页=2108/(2^15)=0
事务id对应在上面页中的偏移量=2108%(2^15)=2108
事务id对应在上面页中第几个的字节=2108/4=527
事务id对应在上面字节中的哪个bit=2108%4=0(表示这个事务在一bytes的第一组bits)

image.png

image.png

在commit后,原本的值应该变为01,但我们查看对应的clog文件部分是00,但是这可能并不代表事务在进程中,因为所有的状态初始值都是00,clog的数据还没有从内存写到磁盘。而且clog分配于共享内存的clog_buffer中,当申请新的CLOG PAGE时所有的clog_buffer都没有刷出脏页,才需要主动选择一个page并调用pg_fsync刷出对应的pg_clog到磁盘中,除此之外,checkpoint会将clog buffer刷到磁盘。因此我这里为了观察选择使用checkpoint。

image.png

此时clog buffer刷到了磁盘,可以看到此事务的状态是01,对照开头的状态,是已经提交的状态。

image.png

上边的例子是一个TransactionIdToByte计算为整数的,当TransactionIdToByte计算带有小数的时候,我们只看整数取整就可以了,例如如下的例子。

image.png

image.png

15从16进制转换成2进制为 0001 0101 ,而上边这个2110的事务,其计算的TransactionIdToBIndex(事务id对应在上面字节中的哪个bit)=2110%4=2,所以他在第3组bit上(取值是0为第一组),为01。因此在这个bytes里,我们的三个事务都是提交的状态。
 

image.png


等到一个byte的四组事务全部都是commited的时候,hexdump -C 0000 -s 527 -n 1查看到的值应该是55,例如下面这种大量的55,如果不是55则表示这一bytes里的四组事务,不是全部提交的,存在IN PROCESS、ABORTED或者SUB_COMMITTED的事务。

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/875648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【 运维这些事儿 】- Gerrit代码审查详

文章目录 背景作用代码审查工具Gerrit镜像构建Dockerfile 部署配置 Gitlab代码同步ssh-agent 相关概念常用命令Git 配置使用 Git Review针对已有项目添加commit-msg,用于自动添加changeId添加源配置 .gitreview备注指定审核人自定义git命令 开发使用代码审查 背景 …

image has dependent child images

问题:很多none的镜像无法被删除 解决过程: 1、通过 docker image prune -f 提示可删除为 0 2、直接进行删除报错: docker rmi 8f5116cbc201Error response from daemon: conflict: unable to delete 8f5116cbc201 (cannot be forced) - im…

面试题-React(一):React是什么?它的主要特点是什么?

探索React:前端开发中的重要角色与主要特点 引言: 在现代前端开发领域,React已经成为最受欢迎和广泛使用的JavaScript库之一。它由Facebook开发并于2013年首次发布。随着时间的推移,React在开发社区中获得了强大的支持和认可。本…

对锁的理解

悲观锁和乐观锁 每次操作的时候都在上锁解锁,能解决并发中的各种问题,不支持并发操作,效率低 每次操作都加上版本号,操作提交的时候会比较数据库版本和目前的版本号是否一致,不一致就会提交失败 表锁和行锁 表锁是对…

Joint HDR Denoising and Fusion: A Real-World Mobile HDR Image Dataset

Abstract 手机已经成为我们日常生活中无处不在、不可或缺的拍照设备,而小光圈和传感器尺寸使得手机更容易受到噪点和过饱和的影响,导致动态范围(LDR)低、画质低。 因此,为手机开发高动态范围(HDR)成像技术至关重要。 然而,现有的…

SpringBean的生命周期和循环依赖

Spring循环依赖 前言 大制作来啦,spring源码篇,很早之前我就想写一系列spring源码篇了,正好最近总是下雨,不想出门,那就让我来带大家走进Spring源码世界吧。 阅读建议 spring源码读起来有点难度,需要多Deb…

性价比最高的护眼灯,护眼台灯应该怎么挑选

随着技术的发展,灯光早已成为每家每户都需要的东西。但是灯光不好可能会对眼睛造成伤害是很多人没有注意到的。现在随着护眼灯产品越来越多,市场上台灯的选择越来越多样化,如何选择一个对眼睛无伤害、无辐射的台灯成为许多家长首先要考虑的问…

limereport报表使用

在这里我使用报表是以报表的形式显示数据库的信息。所以首先需要准备的资料有:limereport源码,还有数据库,我这里使用的是qsqlite数据库。 1、下载limereport报表源码 2、运行自带的案例:demo_r1 3、点击 “Run Report Designer”…

RedisDesktopManager连不上redis问题解决(小白版)

常见问题就是 redis.conf配置文件 a.将port 127.0.0.1这一行注释掉 b.protected-mode保护模式改为no 这个可以看到很多博主都说了,相信都搜到这里来了你们都弄了,我就不详细说了 防火墙开放端口 我说明我自己的问题以及解决方法 1、执行telnet 虚拟…

2023年8月第1~2周大模型荟萃

2023年8月第1~2周大模型荟萃 2023.8.14版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、黑客制造了一款基于 AI 的恶意工具 FraudGPT 早先,有黑客制作了一个“没有道德限制”的 WormGPT 聊天机器人,可以自动生成…

PDF文件限制编辑怎么取消?

PDF文件设置了限制编辑,想要取消PDF文件的限制编辑,很简单,打开PDF编辑器,点击工具栏中的文件,选择属性按钮,进入到熟悉感界面之后,点击安全,然后我们点击权限下拉框,选择…

ModaHub魔搭社区:Milvus Cloud向量数据库可以部分避免AI幻觉

向量数据库的技术优势使其更适合在AI场景下应用,能够为AI的开发、增强内容生成的准确性提供重要的技术支撑。进一步来讲,向量数据库也被看作是大语言模型的记忆与灵魂,对于解决大模型的“幻觉”问题至关重要。 由于大模型是基于已有数据训练而…

TB/TM-商品详情原数据(APP)

一、接口参数说明: item_get_app-获得TB/TMapp商品详情原数据,点击更多API调试,请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_get_app 名称类型必须描述keyString是调用key&…

java+springboot+mysql员工管理系统

项目介绍: 使用javaspringbootmysql开发的员工管理系统(人力资源管理系统),系统包含超级管理员、管理员、员工角色,功能如下: 超级管理员:管理员管理;部门管理;职位管理…

嵌入式面试笔试刷题(day10)

文章目录 前言一、数组和链表的区别二、什么是内存对齐三、IIC的时序四、static作用五、查看tty设备的方法六、查找指定文件命令七、三次握手和四次挥手1.三次握手2.四次挥手 八、半关闭状态九、字节流和数据报总结 前言 本篇文章继续讲解笔试和面试。 一、数组和链表的区别 …

C++ STL stack queue

目录 一.stack 介绍 二.stack 使用 三.stack 模拟实现 普通版本: 适配器版本: 四.queue的介绍 五. queue使用 六.queue模拟实现 七.deque介绍 1.容器适配器 2.deque的简单介绍 3.deque的缺陷 4.为什么选择deque作为stack和queue的底层默认容…

SpringBoot复习:(46)全局的bean懒加载是怎么实现的?

在application.properties中配置: spring.main.lazy-initializationtrue在运行SpringApplication的run方法时,代码如下: 其中调用了prepareContext,prepareContext代码如下: 当在配置文件中配置了spring.main.lazy-initializat…

中国1990-2021连续30年土地利用数据CLCD介绍及下载

CLCD数据介绍 CLCD(China Land Cover Dataset)数据集由武汉大学黄昕老师公布,黄昕老师基于Google Earth Engine上335,709景Landsat数据,制作中国年度土地覆盖数据集(annual China Land Cover Dataset, CLCD),包含1985+1990—2020中国逐年土地覆盖信息。 为此,黄昕老师…

学好Elasticsearch系列-索引的批量操作

本文已收录至 Github,推荐阅读 👉 Java 随想录 先看后赞,养成习惯。 点赞收藏,人生辉煌。 文章目录 基于 mget 的批量查询基于 bulk 的批量增删改增加删除修改 filter_path Elasticsearch 提供了 _bulk API 来执行批量操作&#x…

Web APIs 第六天

正则表达式介绍语法元字符修饰符 一.正则表达式介绍 ① 简介 用来匹配字符串中字符组合的模式在JavaScript中,正则表达式也是对象通常用来查找,替换那些符合正则表达式的文本,许多语言都支持正则表达式 ② 使用场景 验证表单&#xff1a…