PostgreSQL缓存管理

news2024/11/16 17:32:22

缓冲区管理器、存储和后端进程之间的关系

在这里插入图片描述

缓存管理结构

PostgreSQL 缓冲区管理器由buffer table、buffer descriptors和buffer pool组成。buffer pool层存储表和索引等数据文件页,以及空闲空间映射和可见性映射。buffer pool是一个数组,每个槽存储数据文件的一个页面。缓冲池数组的索引称为 buffer_ids。
在这里插入图片描述

  • Buffer pool:存储数据文件页面的数组。数组中的每个槽称为缓冲区_ids。
  • Buffer descriptors:缓冲区描述符数组。每个描述符与一个缓冲池插槽一一对应,并保存相应插槽中存储页面的元数据。
  • Buffer table:哈希表,用于存储已存储页面的缓冲区标签(buffer_tags)与保存已存储页面各自元数据的描述符缓冲区标识(buffer_ids)之间的关系。

Buffer table

缓冲表在逻辑上可分为三个部分:hash function、hash bucket slots和data entries

内置hash function将buffer_tags映射到hash bucket slots。尽管hash bucket的数量多于bucket slots的数量,但仍可能发生碰撞。因此,缓冲区表使用单独的链表方法来解决碰撞问题。当数据条目被映射到同一个缓冲区槽时,这种方法会将条目存储在同一个链表中。

在这里插入图片描述
data entries由两个值组成:页面的缓冲区标签(buffer_tag)和保存页面元数据的描述符的缓冲区 ID

Buffer_tag

/*
 * Buffer tag identifies which disk block the buffer contains.
 *
 * Note: the BufferTag data must be sufficient to determine where to write the
 * block, without reference to pg_class or pg_tablespace entries.  It's
 * possible that the backend flushing the buffer doesn't even believe the
 * relation is visible yet (its xact may have started before the xact that
 * created the rel).  The storage manager must be able to cope anyway.
 *
 * Note: if there's any pad bytes in the struct, InitBufferTag will have
 * to be fixed to zero them, since this struct is used as a hash key.
 */
typedef struct buftag
{
	Oid			spcOid;			/* tablespace oid */
	Oid			dbOid;			/* database oid */
	RelFileNumber relNumber;	/* relation file number */
	ForkNumber	forkNum;		/* fork number */
	BlockNumber blockNum;		/* blknum relative to begin of reln */
} BufferTag;

在 PostgreSQL 中,所有数据文件的每个页面都可以分配一个唯一的标签,即缓冲区标签。当缓冲区管理器收到请求时,PostgreSQL 会使用所需页面的缓冲区标签。

  • specOid: 包含目标页面的关系所属表空间的 OID。
  • dbOid: 包含目标页面的关系所属数据库的 OID。
  • relNumber: 包含目标页面的关系文件的编号。
  • blockNum: 目标页面在关系中的块编号。
  • forkNum:分叉编号: 页面所属关系的 fork 编号。表、自由空间映射和可见性映射的叉号分别定义为 0、1 和 2。

Buffer Descriptor

/*
 * Flags for buffer descriptors
 *
 * Note: BM_TAG_VALID essentially means that there is a buffer hashtable
 * entry associated with the buffer's tag.
 */
#define BM_LOCKED		(1U << 22)	/* buffer header is locked */
#define BM_DIRTY		(1U << 23)	/* data needs writing */
#define BM_VALID		(1U << 24)	/* data is valid */
#define BM_TAG_VALID		(1U << 25)	/* tag is assigned */
#define BM_IO_IN_PROGRESS	(1U << 26)	/* read or write in progress */
#define BM_IO_ERROR		(1U << 27)	/* previous I/O failed */
#define BM_JUST_DIRTIED		(1U << 28)	/* dirtied since write started */
#define BM_PIN_COUNT_WAITER	(1U << 29)	/* have waiter for sole pin */
#define BM_CHECKPOINT_NEEDED	(1U << 30)	/* must write for checkpoint */
#define BM_PERMANENT		(1U << 31)	/* permanent buffer (not unlogged,
						 * or init fork) */

#define PG_HAVE_ATOMIC_U32_SUPPORT
typedef struct pg_atomic_uint32
{
	volatile uint32 value;
} pg_atomic_uint32;


typedef struct BufferDesc
{
	BufferTag	tag;			/* ID of page contained in buffer */
	int		buf_id;			/* buffer's index number (from 0) */

	/* state of the tag, containing flags, refcount and usagecount */
	pg_atomic_uint32 state;

	int		wait_backend_pgprocno;	/* backend of pin-count waiter */
	int		freeNext;		/* link in freelist chain */
	LWLock		content_lock;	/* to lock access to buffer contents */
} BufferDesc;

  • tag :是相应缓冲池插槽中存储页面的缓冲区标签。
  • buf_id :标识描述符。
  • content_lock :是一个轻量级锁,用于控制对相关存储页面的访问。
  • freeNext :是指向生成自由列表的下一个描述符的指针。
  • states :可以保存相关存储页面的多个状态和变量,如 refcount 和 usage_count。

descriptor states

  • Empty:当相应的缓冲池槽没有存储页面时(即 refcount 和 usage_count 为 0),该描述符的状态为空。
  • Pinned:当相应的缓冲池槽存储了一个页面,且有任何 PostgreSQL 进程正在访问该页面(即 refcount 和 usage_count 大于或等于 1)时,该缓冲区描述符的状态就会被钉住。
  • Unpinned:当相应的缓冲池插槽存储了一个页面,但没有 PostgreSQL 进程访问该页面(即 usage_count 大于或等于 1,但 refcount 为 0)时,该缓冲区描述符的状态为Unpinned。

Buffer Descriptors Layer

Buffer Descriptors集合构成一个数组,在本文档中称为Buffer Descriptors Layer。

服务器启动时,所有缓冲区描述符的状态都是空的。在 PostgreSQL 中,这些描述符由一个称为 freelist 的链表组成
在这里插入图片描述
加载第一页步骤:
(1) 从自由列表顶端读取一个空描述符,并将其固定(即将其 refcount 和 usage_count 增加 1)。
(2) 在缓冲区表中插入一个新条目,将第一页的标签映射到检索到的描述符的缓冲区 ID。
(3) 将新页面从存储器加载到相应的缓冲池插槽中。
(4) 将新页面的元数据保存到检索到的描述符中。

在这里插入图片描述
从freelist中获取的描述符始终保留页面的元数据。换句话说,非空描述符一旦被使用,就不会返回freelist。但是,当出现以下情况之一时,相应的描述符会被再次添加到自由列表中,描述符状态也会被设置为 “NULL”:

  1. 表或索引被删除。
  2. 数据库被删除。
  3. 使用 VACUUM FULL 命令清理表或索引。

buffer pool

缓冲池是一个简单数组,用于存储表和索引等数据文件页。缓冲池数组的索引称为 buffer_ids。

缓冲池槽的大小为 8 KB,相当于一个页面的大小。因此,每个槽可以存储整个页面。

Buffer Manager Works

Accessing a Page Stored in the Buffer Pool

  1. 创建所需页面的缓冲区标签,并使用哈希函数计算包含已创建缓冲区标签相关条目的哈希桶槽。
  2. 以共享模式获取覆盖所获哈希桶槽的 BufMappingLock 分区(该锁将在步骤 (5) 中释放)。
  3. 查找标签为 "Tag_C "的条目,并从条目中获取缓冲区 ID。在本例中,缓冲区 ID 为 2
  4. 将 buffer_id 2 的缓冲区描述符钉住,同时将该描述符的 refcount 和 usage_count 增加 1
  5. 释放 BufMappingLock。
  6. 访问缓冲区标识为 2 的缓冲区池插槽。

在这里插入图片描述
然后,当从缓冲池插槽中的页面读取行时,PostgreSQL 进程会获取相应缓冲区描述符的共享 content_lock。因此,缓冲池插槽可同时被多个进程读取。

当向页面插入(以及更新或删除)记录时,PostgreSQL 进程会获取相应缓冲区描述符的独占内容锁。(注意,页面的 dirty 位必须设置为 “1”)。

访问页面后,相应缓冲区描述符的 refcount 值将减少 1。

Loading a Page from Storage to Empty Slot

将页面从存储器加载到空槽
(1) 查找缓冲区表(假设未找到)。

  1. 创建所需页面的缓冲区标记(本例中缓冲区标记为 “Tag_E”)并计算哈希桶槽。
  2. 以共享模式获取 BufMappingLock 分区。
  3. 查找缓冲区表。(根据假设未找到)。
  4. 释放 BufMappingLock。

(2) 从 freelist 中获取空缓冲区描述符,并将其固定。在本例中,获取的描述符的 buffer_id 为 4。
(3) 以独占模式获取 BufMappingLock 分区。(该锁将在步骤 (6) 中释放)。
(4) 创建一个包含缓冲区标记 "Tag_E "和缓冲区 ID 4 的新数据条目。 将创建的条目插入缓冲区表。
(5) 将所需的页面数据从存储器加载到缓冲区池插槽中,缓冲区标识为 4,具体步骤如下:

  1. 在 9.5 或更早版本中,获取相应描述符的独占 io_in_progress_lock 2.
  2. 将相应描述符的 io_in_progress 位设置为 “1”,以防止其他进程访问。
  3. 将所需的页面数据从存储器加载到缓冲池插槽。
  4. 更改相应描述符的状态:将 io_in_progress 位设置为 “0”,将有效位设置为 “1”。
  5. 在 9.5 或更早版本中,释放 io_in_progress_lock 。

(6) 释放 BufMappingLock。
(7) 访问 buffer_id 为 4 的缓冲池插槽。

在这里插入图片描述

Loading a Page from Storage to a Victim Buffer Pool Slot

假设所有缓冲池插槽都被页面占用,但所需页面未被存储。缓冲区管理器执行以下步骤:
(1)创建所需页面的缓冲区标签,并查找缓冲区表。
(2)使用时钟扫描算法选择一个victim buffer pool slot。从缓冲区表中获取包含victim pool slot buffer_ID 的旧条目,并将victim buffer pool slot固定在缓冲区描述符层中。
(3)如果是脏数据,则清除(写入并同步)受害页数据;否则继续执行步骤 (4)。
在用新数据覆盖之前,必须先将脏页写入存储器。刷新脏页的步骤如下:

  • 获取具有 5 的描述符的共享context_lock和独占的 io_in_progress 锁(在步骤 6 中释放)。
  • 更改相应描述符的状态;将 io_in_progress 位设置为 “1”,将 just_dirtied 位设置为 “0”。
  • 根据情况调用 XLogFlush() 函数,将 WAL 缓冲区中的 WAL 数据写入当前的 WAL 段文件。
  • 将victim页面数据刷新到存储中。
  • 更改相应描述符的状态;将 io_in_progress 位设置为 “0”,将 valid 位设置为 “1”。
  • 释放 io_in_progress 和 content_lock 锁。

(4)以独占模式获取包含旧条目插槽的旧 BufMappingLock 分区。
(5)获取新的 BufMappingLock 分区,并在缓冲区表中插入新条目

  • 创建由新缓冲区标签 "Tag_M "和victim buffer_ID 组成的新条目。
  • 获取新的 BufMappingLock 分区,该分区以独占模式覆盖了包含新条目的插槽。
  • 将新条目插入缓冲区表。

(6) 从缓冲区表中删除旧条目,并释放旧的 BufMappingLock 分区。
(7) 将所需的页面数据从存储器加载到受害者缓冲区插槽。然后,更新带有 buffer_id 5 的描述符的标志;将 dirty 位设置为 0,并初始化其他位。
(8) 释放新的 BufMappingLock 分区。
(9) 访问带有 buffer_id 5 的缓冲池槽。

在这里插入图片描述
在这里插入图片描述

Page Replacement Algorithm: Clock Sweep

将缓冲区描述符想象成一个循环列表。nextVictimBuffer 是一个无符号 32 位整数,始终指向其中一个缓冲区描述符,并顺时针旋转。算法的伪代码和说明如下:

     WHILE true
(1)     Obtain the candidate buffer descriptor pointed by the nextVictimBuffer
(2)     IF the candidate descriptor is unpinned THEN
(3)	       IF the candidate descriptor's usage_count == 0 THEN
	            BREAK WHILE LOOP  /* the corresponding slot of this descriptor is victim slot. */
	       ELSE
		    Decrease the candidate descriptpor's usage_count by 1
               END IF
         END IF
(4)     Advance nextVictimBuffer to the next one
      END WHILE 
(5) RETURN buffer_id of the victim

(1) 获取 nextVictimBuffer 指向的候选缓冲区描述符。
(2) 如果候选缓冲区描述符未锁定,则执行步骤 (3)。否则,进入步骤 (4)。
(3) 如果候选描述符的使用次数为 0,则选择该描述符的相应槽作为victim,并继续执行步骤 (5)。否则,将该描述符的使用次数减 1,并继续执行步骤 (4)。
(4) 将 nextVictimBuffer 提前到下一个描述符(如果在末尾,则绕一圈),然后返回步骤 (1)。重复上述步骤,直到找到victim。
(5) 返回victim的buffer_ID。

在这里插入图片描述

  • nextVictimBuffer 指向第一个描述符(buffer_id 1)。但是,由于该描述符已被钉住,因此会被跳过。
  • nextVictimBuffer 指向第二个描述符(buffer_id 2)。因此,使用次数减少 1,nextVictimBuffer 进入第三个候选对象。
  • nextVictimBuffer 指向第三个描述符(buffer_id 3)。因此,它是本轮的victim。

每当 nextVictimBuffer 扫描一个未固定的描述符时,其 usage_count 就会减少 1。 因此,如果缓冲池中存在未固定的描述符,该算法总能通过旋转 nextVictimBuffer 找到一个 usage_count 为 0 的受害者。

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

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

相关文章

Redis RedLock算法和底层源码分析

Redlock红锁算法 官网地址&#xff1a;Distributed Locks with Redis | Redis 为什么要使用RedLock&#xff1f; 解释&#xff1a; 线程 1 首先获取锁成功&#xff0c;将键值对写入 redis 的 master 节点&#xff0c;在 redis 将该键值对同步到 slave 节点之前&#xff0c;mas…

vue+express、gitee pm2部署轻量服务器

一、代码配置 前后端接口都保持 127.0.0.1:3000 vue创建文件 pm2.config.cjs module.exports {apps: [{name: xin-web, // 应用程序的名称script: npm, // 启动脚本args: run dev, // 启动脚本的参数cwd: /home/vue/xin_web, // Vite 项目的根目录interpreter: none, // 告诉…

java基础-并发编程-CountDownLatch(JDK1.8)源码学习

CountDownLatch方法调用与类关系图 一、初始化&#xff1a;public CountDownLatch(int count) public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync new Sync(count);}Sync(int count) {// 将参数…

pte初步认识学习

我们的时间的确很少&#xff0c;但是我们每天都乐意将珍贵的时间浪费在大量毫无意义的事情上 目录 pte介绍 PTE口语评分规则 pte架构 计算机科学23 QS排名 《芭比》 pte介绍 PTE口语评分规则 有抑扬顿挫 对于连读 不能回读 native pte对于个别单词没有读好&#xff0c…

JSP 学习笔记(基础)

出现背景&#xff1a; 由于 Servlet 输出 HTML 比较困难&#xff0c;所以出现了 JSP 这个代替品。 特点&#xff1a; 基于文本&#xff0c;HTML 和 Java 代码共同存在&#xff08;用 write() 来写 HTML 标签&#xff09;其本身就是个被封装后的 Servlet&#xff08;被编译为…

JavaScript的BOM操作

一、BOM 1.认识BOM BOM&#xff1a;浏览器对象模型&#xff08;Browser Object Model&#xff09; 简称 BOM&#xff0c;由浏览器提供的用于处理文档&#xff08;document&#xff09;之外的所有内容的其他对象&#xff1b;比如navigator、location、history等对象&#xff…

Android 使用Camera1实现相机预览、拍照、录像

1. 前言 本文介绍如何从零开始&#xff0c;在Android中实现Camera1的接入&#xff0c;并在文末提供Camera1Manager工具类&#xff0c;可以用于快速接入Camera1。 Android Camera1 API虽然已经被Google废弃&#xff0c;但有些场景下不得不使用。 并且Camera1返回的帧数据是NV21…

JSP ssm 零配件管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java ssm 零配件管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用…

ThinkPHP 5.0通过composer升级到5.1,超级简单

事情是这样的&#xff0c;我实现一个验证码登录的功能&#xff0c;但是这个验证码的包提示tp5的版本可以是5.1.1、5.1.2、5.1.3。但我使用的是5.0&#xff0c;既然这样&#xff0c;那就升个级呗&#xff0c;百度了一下&#xff0c;结果发现大部分都是讲先备份application和修改…

python之pyQt5实例:PyQtGraph的应用

1、显示逻辑 "MainWindow": "这是主窗口&#xff0c;所有的其他组件都会被添加到这个窗口上。", "centralwidget": "这是主窗口的中心部件&#xff0c;它包含了其他的部件。","pushButton": "这是一个按钮&#xff0c…

算法刷题 week3

这里写目录标题 1.重建二叉树题目题解(递归) O(n) 2.二叉树的下一个节点题目题解(模拟) O(h) 3.用两个栈实现队列题目题解(栈&#xff0c;队列) O(n) 1.重建二叉树 题目 题解 (递归) O(n) 递归建立整棵二叉树&#xff1a;先递归创建左右子树&#xff0c;然后创建根节点&…

贪心算法的思路和典型例题

一、贪心算法的思想 贪心算法是一种求解问题时&#xff0c;总是做出在当前看来是最好的选择&#xff0c;不从整体最优上加以考虑的算法。 二.用贪心算法的解题策略 其基本思路是从问题的某一个初始解出发一步一步地进行&#xff0c;根据某个优化测度&#xff0c;每一步都要确保…

Idea 下载不了源码 cannot download source

一、打开Terminal (AltF12)&#xff0c;找到项目具体模块所在的文件夹&#xff0c;输入一下指令 mvn dependency:resolve -Dclassifiersources 如果你的idea 终端无法使用mvn指令&#xff0c;要配置你idea中的maven的环境变量&#xff1a; 1、找到maven在idea中的位置&#xf…

Linux备份策略:保证数据安全

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

第十章 数据库恢复技术

第十章 数据库恢复技术 10.1 事务的基本概念 事务 事务是用户定义的一个数据库操作序列&#xff0c;这些操作要么全做&#xff0c;要么全不做&#xff0c;是一个不可分割的工作单位。例事务的特性&#xff08;ACID特性&#xff08;ACID properties&#xff09;&#xff09; 原…

【测试开发】概念篇 · 测试相关基础概念 · 常见开发模型 · 常见测试模型

【测试开发】概念篇 文章目录 【测试开发】概念篇1. 什么是需求1.1 需求的定义1.2 为什么有需求1.3 测试人员眼里的需求1.4 如何深入了解需求 2. 什么是测试用例2.1 为什么有测试用例2.2 练习>手机打电话 3. 什么是bug4. 开发模型和测试模型4.1 软件生命周期4.2 开发模型4.3…

七、线性规划问题

文章目录 1、线性规划问题定义2、单纯形算法THE END 1、线性规划问题定义 \qquad 线性规划问题的一般表示形式如下所示&#xff1a;假设现有 n n n个变量&#xff0c; m m m个约束&#xff0c;令最大化(或者最小化) c 1 x 1 c 2 x 2 . . . c n x n c_1x_1c_2x_2...c_nx_n c1…

IDEA中创建Java Web项目方法2

以下过程使用IntelliJ IDEA 2021.3 一、创建Maven项目 1. File -> New -> Projects... 2. 选择Maven&#xff0c;点击Next 3. 输入项目名称&#xff0c;Name: WebDemo3。点击 Finish&#xff0c;生成新的项目 二、添加框架支持 1. 在项目名上右键&#xff0c;选择 A…

快速搭建SpringBoot3.x项目

快速搭建SpringBoot3.x项目 写在前面一、创建项目二、配置多环境三、连接数据库查询数据3.1 新建数据库mybatisdemo并且创建sys_user表3.2 创建实体类3.2 创建Mapper接口3.3 添加mybatis.xml文件3.4 新建service 接口及实现类3.5 创建Controller 四、封装统一结果返回4.1 定义 …

计算平均值

任务描述 编程实现&#xff1a;编写程序实现如下功能&#xff1a;通过键盘&#xff0c;用指针输入10个元素的值&#xff0c;再通过指针计算各元素的平均值&#xff0c;输出平均值。 测试说明 平台会对你编写的代码进行测试&#xff1a; 测试样例1&#xff1a; 测试输入&…