<PostgreSQL数据库内核分析>之第三章:存储管理

2024-06-08 05:18

本文主要是介绍<PostgreSQL数据库内核分析>之第三章:存储管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 一、存储管理器的体系结构
    • 二、外存管理
      • 1.表和元组的组织方式
      • 2.磁盘管理器
    • 3.内存管理

一、存储管理器的体系结构

存储管理器是DBS与物理存取设备的接口

  • 存储管理器的体系结构如下
    (1)本地内存是每个后台进程所专有,存储属于该进程的高速缓存Cache、事务管理信息、进程信息等
    (2)内存上下文:用于统一管理内存的分配和回收
    (3)PG中每个表都用一个表文件来存储,并以表的OID命名,若超出OS文件大小限制,PG会将其切分为多个文件来存储;
    每个表除了表文件外,还拥有两个附属文件:
    可见性映射表文件VM(作用:加快VACUUM的执行速度)
    空闲空间映射表文件FSM作用:(管理表文件的空闲空间)。
    在这里插入图片描述
  • 存储管理器使用了虚拟文件描述符机制VFD,使得后台进程可以打开“无限多个”文件。
  • 缓冲池:进程间共享缓冲池
    (1)为了防止多个进程并发访问共享内存中的数据时产生的冲突,PG提供了轻量级锁,用于支持对共享内存中同一数据的互斥访问
  • 缓冲池:进程私有的本地缓冲池
    (1)PG中的数据在内存中是以页面块的形式存在,每个表空间由多个BLCKSZ(一个可配置的常量)字节大小的文件块组成,每个文件块可以包含多个元组,但是PG不支持元组的跨块存储,每个元组最大为MaxHeadTupleSize,保证了每个文件块中存储的是多个完整的元组。
    (2)表文件以文件块为单位读入内存中,每一个文件块在内存中形成一个页面块。
    (3)PG在内存中开辟了缓冲区域(缓冲池):磁盘上的文件块读入内存后被存放在缓冲区中,缓冲池被划分为与文件块的尺寸相同的若干个固定大小的缓冲区,一个标准缓冲块的默认大小是8K。

一个PG进程(postmaster、postgres)从数据库读写一个元组的流程:

  • 进程的本地内存有2种Cache:存储系统表元组、存储系统表的基本信息

  • PG的共享缓冲池与本地缓冲池:本地缓冲池用于缓冲临时表数据以及其他进程不可见的数据

  • 若缓冲池中没有包含所需元组的缓冲块,则需通过存储介质管理器SMGR来从存储介质中读取,并写入到缓冲区中。

  • 磁盘管理器负责管理所有存储在磁盘上的文件的操作

  • 虚拟文件管理:虚拟文件描述符VFD机制。VFD通过合理使用有限个实际文件描述符来满足无限的VFD访问需求,VFD通过维持一个LRU池来管理FD
    在这里插入图片描述

  • 在PG中,为每个表增加了一个附属文件,即空闲空间映射表FSM,用于记录每个表文件中块的空闲空间大小,通过一定的查找机制和数据组织实现了文件块的快速选择

  • PG使用标记删除的方式来处理元组的删除,即:对元组打上删除标记,而非从物理上删除元组。
    元组的物理删除是由VACUUM机制来完成的,由于VACUUM操作时会去遍历所有文件块,去查找被删除的元组效率低,所以也为表设计了可见性映射表VM,用以加快查找的速度。

二、外存管理

每个表文件在磁盘中都以一定的结构进行存储

  • 外存管理体系结构
    在这里插入图片描述

1.表和元组的组织方式

PG中同一个表中的元组是顺序依次插入到表文件中的。

  • 元组之间不关联的表文件,称之为堆文件。
    PG包括四种堆文件:普通堆ordinary cataloged heap、临时堆temporary heap、序列sequence relation(特殊的单行表)、TOAST表(toast table)。
    (1)临时堆仅在会话过程中创建、在会话结束时自动删除;
    (2)序列是一种元组值自动增长的特殊堆
    (3)TOAST表其实也是一种普通堆,但是它被专门用于存储变长数据。
  • 堆文件的物理结构如下,
    (1)每个堆文件由多个文件块组成
    (2)Linp是ItemIdData类型的数组,每一个ItemIdData结构用来指向文件块中的一个元组
    (3)Freespace是空闲空间,新插入页面的元组都从这部分空间来分配
    (4)Special space是特殊空间,用于存放与索引方法相关的特定数据
    (5)元组信息:存放元组的实际数据和元组的头部信息(事务ID+命令ID等信息)
    在这里插入图片描述
  • 从堆中删除一个元组:采用标记删除的方法,为每个元组使用额外的数据位作为删除标记。
    当删除元组时,只需设置相应的删除标记(在元组头部,记录了删除这个元组的事务ID和命令ID),即可设置相应的删除标记。
    若上述两个ID有效,则表明该元组应该被删除。

2.磁盘管理器

磁盘管理器并非对磁盘上的文件直接进行操作,而是通过VFD机制进行文件操作。

  • 因为一个进程打开的fd数量有限(受限于OS规定的最大值),所以设计了VFD机制
  • VFD、真实文件描述符、文件之间的关系
    (1)虚拟文件描述符是指:一个叫做VFD的数据结构,其中记录了操作系统为文件分配的真实fd;
    (2)多个进程打开同一个文件,那么每个进程将获得一个真实fd,每个真实fd对应一个VFD;
    在这里插入图片描述

LRU池

  • 每一个PG后台进程都使用一个LRU(Last Recently Used,最近最少使用)池来管理所有已经打开的VFD,池中每一个VFD都对应物理上已经打开的文件;
  • 每个进程都拥有其私有的LRU池和一系列VFD,进程需要打开文件时都是从自己私有的LRU池中申请VFD
    (1)在LRU池中,使用替换最长时间未使用的VFD策略
    (2)进程在VfdCache上保持了两个链表,一个是LRU池(双向链表,通过Vfd数据结构的IruMoreRecently属性和IruLessRecently属性链接),另一个是FreeList(空闲链表,记录了所有可被分配的VFD,通过Vfd数据结构中的nextFree属性来连接)。
    (3)PG中将一个进程当前正打开的所有文件的VFD都连成一个环,即LRU池;
    每个方框代表一个VFD(一个VfdCache数组元素),方框里的符号表示该VFD在数组中的下标。
    在LRU池中,每一个VFD都通过指针链接两个VFD,通过指针IruMoreRecently链接最近最常用的VFD,通过指针IruLessRecently链接最近不长使用的VFD;
    (4)LRU池的大小与操作系统对于进程打开文件数的限制保持一致,在postmaster进程的启动过程中会调用max_safe_fds函数来检测操作系统限制;
    (5)插入:新的VFD在VFD[0]后插入;
    删除:在LRU池中删除VFD;若LRU池已满,而又要打开新的fd,则将池中末尾的VFD删除(最少使用的VFD)
    在这里插入图片描述

空闲空间映射表FSM

  • 对于每个表文件(包括系统表在内),同时创建一个名为“关系表OID_fsm”的文件,用以记录该表的空闲空间大小,称之为空闲空间映射表文件FSM。
  • FSM物理块与逻辑地址对照
    (1)为了实现快速查找,FSM文件不太大且使用了树结构;
    FSM中存储的不是实际的文件块空间的大小,而是仅用一个字节来记录,该字节的值用于描述对应文件块中空闲空间的范围:
    所以对于任意一个表块,根据该字节的值就可以知道这个表块中空闲空间的范围;
    对于任意一个表块,根据其空闲空间的大小可以计算出它对应的FSM字节的取值,eg,N字节空闲空间的表块,FSM中记录的值为(31+N)/32;
    在这里插入图片描述
    (2)FSM块之间使用了一个三层树结构:
    第0层和第1层是辅助层:快速定位满足需要表块的FSM块
    第2层FSM块:实际存放各表块的空闲空间值;
    每一个FSM快内构成一个局部的最大堆二叉树,每个叶子节点表示一个表块的空闲空间值,按照从左至右的顺序,所有第2层FSM块中的叶子节点排列起来就一一对应了表文件中的每一个表块。
    第1层FSM块中的叶子节点从左至右顺序对应第2层FSM块的跟节点,第0层与第一层关系类似。
    (3)每个FSM块大小默认是8KB,出去头部等信息,每个叶子节点用一个字节记录,假设一个FSM块内可以保存4000个叶子节点,所以,一个FSM文件(有三个FSM块:0层FSM块、1层FSM块、2层FSM块)可以记录4000^3个叶子节点(表块);
    这是远远大于2^32,单个表的最大块数(PG的块号长度为32bit,所以单个表最多只能有2^32个块)
    (4)FSM文件中第一个文件块中二叉树(0层的0号FAM块)根节点存储的是所有表块空闲空间的最大值。
    在这里插入图片描述

可见性映射表vm

  • PG为了实现多版本的并发控制,当事务删除或者更新元组时,并非从物理上删除,而是通过将其标记为无效的方式进行标记删除,最终对这些无效的清理操作则需调用VACUUM(VACUUM查找包含无效元组的文件块)
  • VACUUM有2种情况方式:快速清理Lazy VACUUM和完全清理Full VACUUM。
    vm仅在Lazy VACUUM中使用到,Full VACUUM由于要执行跨块清理等操作,需要对整个表文件进行扫描,此时vm文件作用不大。
  • vm文件也被划分为若干个文件块(vm块),vm块中除了必要的标记信息外,其他的每一位都对应一个表块,当表块中所有的元组对当前的事务都是可见的时,表块对应的位才设为1
    当对某个表块中的元组进行更新或者删除后,那么该表块在vm文件中的对应位置的标志位将被置0。
  • vm文件仅仅是作为一个提示hint,加快vacuum的速度
    在这里插入图片描述

大数据存储

  • toast机制(the oversi-attribute storage technique,数据压缩和线外存储)和大对象机制(使用一个专门的系统表来存储大对象数据)
  • toast机制
    (1)这类数据类型必须有变长(varlena类型),eg:text类型,只有在准备向支持TOAST的属性中存储超过BLOCKSZ/4字节(2KB),TOAST机制才会被触发。
    TOAST机制会试图对将要存储的数据进行压缩或线外存储数据(即:把数据存储在其他的表中),直到数据比BLOCKSZ/4字节短,或者无法得到更好的结果的时候才停止
    (2)如果一个表中的属性是可以TOAST的,那么该表的将有一个关联的TOAST表。
    (3)TOAST机制优点:可以有效地节省查询时所占的内存空间,TOAST数据只是在向用户显示结果时才被取出来,在查询过程中并不需要检查TOAST数据的具体取值
    (4)TOAST技术保障了PG中的元组大小足以存放在一个文件块中,so,TOAST技术必须被设置为一种系统自动处理的机制。
    TOAST机制主要集中于变长的数据类型。
  • 大对象
    (1)PG的大对象存储机制可以支持三种数据类型的存储:
    二进制大对象BLOB:eg:图片、视频
    字符大对象CLOB:存储大的单字节字符集数据,eg文档
    双字节字符大对象DBCLOOB:存储大的双字节字符集数据,eg变长双字节字符图形字符串
    (2)一个大对象会被分成若干个元组存放在系统表pg_largeobject中,每一个元组也称为一个页面。
    一个元组大小为2KB,设置成2KB的原因是:可以触发TOAST的压缩机制。

TOAST与对象的区别

  • TOAST是可变长数据类型的一种大数据存储机制,属于自动触发机制;
    大对象属于用于手动调用机制
  • TOAST中的数据不能丢失,一旦丢失则报错;大对象中运行数据丢失,若丢失,则用0替代;
  • 文件不适合使用TOAST技术,原因:二进制读-》将二进制当做字符串存储到变长数据类型的属性中-》转变成文件;大对象操作是将文件作为一个对象存储到大对象表中,读取时直接读取成一个文件。
  • TOAST和大对象操作保存大数据时,都是采用了将数据切片成了片段存储到表中的方式。

3.内存管理

尽可能让使用的文件停留在内存中,这样就能有效地减少磁盘I/O代价。

  • PG内存管理
    在这里插入图片描述
  • 一个内存上下文实际上就相当于一个进程环境
    PG每个子进程都拥有多个私有的内存上下文,每个子进程的内存上下文组成一个树形结构。
    在这里插入图片描述
    高速缓存
  • 当数据库访问表时,需要表的模式信息,比如表的列属性、OID、统计信息等。
    PG将表的模式信息存放在系统表中,因此要访问表,就需要首先在系统表中取得表的模式信息。
    PG设立了高速缓存Cache来提高对系统表和普通表模式的访问效率。
    Cache中包括一个系统表元组SysCache和一个表模式信息RelCache:
    SysCache主要用于缓存系统表元组;
    RelCache存放的不是元组,而是RelationData数据结构;
  • 每一个PG进程都维护着自己的SysCache和RelCache。

Cache同步

  • PG中,每一个进程都有自己的Cache。同一个进程表在不同的进程中都有对应的Cache来缓存它的元组。桶一个系统表的元组可能被多个进程的Cache所缓存,当其中某个Cache中的一个元组被删除或更新,需要通知其他进程对其Cache进行同步。
    PG中会记录下已被删除的无效元组,并通过共享消息队列的方式在进程之间传递消息,收到无效消息的进程同步地把无效元组(RelationData结构,因为RelCache缓存的是一个RelationData数据结构)才能够自己的Cache中删除。
  • 当一个元组被删除或者更新时,在同一个SQL命令的后续执行步骤中,我们依然认为该元组是有效的,直到下一个命令开始或者事务提交时改动才生效。
    在命令的边界,旧元组变为失效,同时新元组变为有效。so,当执行heap_delete或者heap_update时,不能简单地刷新cache,正确的做法是:保持一个无效链表用于记录元组的delete/update操作。
    事务完成后,将无效链表中的信息广播该事务过程中产生的无效信息,其他进程通过消息队列读取无效信息对各自的Cache进行刷新。
    当子事务提交时,只需要将该事务产生的无效消息提交到父事务,最后由最上层的事务广播无效消息。

缓冲池管理

这篇关于<PostgreSQL数据库内核分析>之第三章:存储管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041293

相关文章

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Ubuntu中远程连接Mysql数据库的详细图文教程

《Ubuntu中远程连接Mysql数据库的详细图文教程》Ubuntu是一个以桌面应用为主的Linux发行版操作系统,这篇文章主要为大家详细介绍了Ubuntu中远程连接Mysql数据库的详细图文教程,有... 目录1、版本2、检查有没有mysql2.1 查询是否安装了Mysql包2.2 查看Mysql版本2.

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

Win11安装PostgreSQL数据库的两种方式详细步骤

《Win11安装PostgreSQL数据库的两种方式详细步骤》PostgreSQL是备受业界青睐的关系型数据库,尤其是在地理空间和移动领域,:本文主要介绍Win11安装PostgreSQL数据库的... 目录一、exe文件安装 (推荐)下载安装包1. 选择操作系统2. 跳转到EDB(PostgreSQL 的

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3