PostgreSQL技术内幕5:PostgreSQL存储引擎从磁盘到内存的读取

news2024/9/22 3:41:02

文章目录

    • 0.简介
    • 1.背景知识
      • 1.1 计算机存储结构
      • 1.2 数据库常见的磁盘和内存访问形式
    • 2. 整体获取层次
    • 3.元组介绍
    • 4. Buffer管理
      • 4.1 Buffer组成
      • 4.2 修改后落盘
      • 4.3 获取buffer页的流程
    • 5.存储管理器(SMGR)
    • 6.磁盘管理器(MD)
    • 7.虚拟文件管理器(VFD)
    • 8.物理文件存储介绍
    • 9. 总结

0.简介

本篇内容介绍PG从磁盘到内存的加载流程,经过那些层级,各层级作用以及源码分析,主要包括共享缓存(Buffer),存储管理器,磁盘管理器,虚拟文件管理器以及部分物理文件介绍。

1.背景知识

1.1 计算机存储结构

计算机存储层级如下图,速度层级越往下越慢。
在这里插入图片描述

1.2 数据库常见的磁盘和内存访问形式

常见的访问方式为在磁盘以page形式存储,在内存中存储到Buffer Pool中,如下图:
在这里插入图片描述

2. 整体获取层次

PG整体获取一个元组涉及的层级如图,下面将对每一层进行详细说明:
在这里插入图片描述

3.元组介绍

Tuple结构如下
在这里插入图片描述
Header信息:

struct HeapTupleHeaderData
{
    union
    {
        HeapTupleFields t_heap;
        DatumTupleFields t_datum;
    }           t_choice;

    ItemPointerData t_ctid;     /* current TID of this or newer tuple (or a
                                 * speculative insertion token) */

    /* Fields below here must match MinimalTupleData! */

#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
    uint16      t_infomask2;    /* number of attributes + various flags */

#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
    uint16      t_infomask;     /* various flag bits, see below */

#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
    uint8       t_hoff;         /* sizeof header incl. bitmap, padding */

    /* ^ - 23 bytes - ^ */

#define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
    bits8       t_bits[FLEXIBLE_ARRAY_MEMBER];  /* bitmap of NULLs */

    /* MORE DATA FOLLOWS AT END OF STRUCT */
};

4. Buffer管理

4.1 Buffer组成

PG中buffer 由三部分组成,如下图,创建函数可见CreateSharedMemoryAndSemaphores。
在这里插入图片描述
1)Buffer table layer:一个hash表,记录buffer描述符和buff pool的映射信息(即为tag->bufferid(buffer pool的下标)的映射)
2)Buffer descriptors layer:buffer描述符
3)Buffer pool:真正的buffer数据

Buffer descriptors结构如下:


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_pid;   /* backend PID of pin-count waiter */
    int         freeNext;       /* link in freelist chain */

    LWLock      content_lock;   /* to lock access to buffer contents */
} BufferDesc;
//对应到页
typedef struct buftag
{
    RelFileNode rnode;          /* physical relation identifier */
    ForkNumber  forkNum;    /*file type*/
    BlockNumber blockNum;       /* blknum relative to begin of reln */
} BufferTag;

//唯一确定表
typedef struct RelFileNode
{
    Oid         spcNode;        /* tablespace */
    Oid         dbNode;         /* database */
    Oid         relNode;        /* relation */
} RelFileNode;

4.2 修改后落盘

 可见MarkBufferDirty函数,其主要作用就是增加该页的BM_DIRTY状态,该状态的页面淘汰前会落盘。

4.3 获取buffer页的流程

可见ReadBuffer_common函数
在这里插入图片描述
4.4 Buffer的淘汰策略

PG中使用的使用ClockSweep淘汰策略,其相关结构如下

typedef struct
{
    /* Spinlock: protects the values below */
    slock_t     buffer_strategy_lock;

    /*
     * Clock sweep hand: index of next buffer to consider grabbing. Note that
     * this isn't a concrete buffer - we only ever increase the value. So, to
     * get an actual buffer, it needs to be used modulo NBuffers.
     */
    pg_atomic_uint32 nextVictimBuffer;

    int         firstFreeBuffer;    /* Head of list of unused buffers */
    int         lastFreeBuffer; /* Tail of list of unused buffers */

    /*
     * NOTE: lastFreeBuffer is undefined when firstFreeBuffer is -1 (that is,
     * when the list is empty)
     */

    /*
     * Statistics.  These counters should be wide enough that they can't
     * overflow during a single bgwriter cycle.
     */
    uint32      completePasses; /* Complete cycles of the clock sweep */
    pg_atomic_uint32 numBufferAllocs;   /* Buffers allocated since last reset */

    /*
     * Bgworker process to be notified upon activity or -1 if none. See
     * StrategyNotifyBgWriter.
     */
    int         bgwprocno;
} BufferStrategyControl;

buffer descriptor是循环链表,数组标识usage_count,nextVictimBuffer是32位usigned int,总是指向某个buffer descriptor并按顺时针顺序旋转。淘汰页面代码:StrategyGetBuffer。主要做到事情就是引用数为0,使用数减一,使用为0可以淘汰。

5.存储管理器(SMGR)

 负责统一管理不同存储介质的对接,通过虚函数表来调用。每个backend使用SMGR的SMgrRelationHash管理SMgrRelationData,实现加速访问,SMgrRelationData记录一张打开的表的信息。
typedef struct SMgrRelationData
{
  /* rnode is the hashtable lookup key, so it must be first! */
  RelFileNodeBackend smgr_rnode;  /* relation physical identifier */

  /* pointer to owning pointer, or NULL if none */
  struct SMgrRelationData **smgr_owner;

  /*
   * These next three fields are not actually used or manipulated by smgr,
   * except that they are reset to InvalidBlockNumber upon a cache flush
   * event (in particular, upon truncation of the relation).  Higher levels
   * store cached state here so that it will be reset when truncation
   * happens.  In all three cases, InvalidBlockNumber means "unknown".
   */
  BlockNumber smgr_targblock; /* current insertion target block */
  BlockNumber smgr_fsm_nblocks;  /* last known size of fsm fork */
  BlockNumber smgr_vm_nblocks;  /* last known size of vm fork */

  /* additional public fields may someday exist here */

  /*
   * Fields below here are intended to be private to smgr.c and its
   * submodules.  Do not touch them from elsewhere.
   */
  int      smgr_which;    /* storage manager selector */

  /*
   * for md.c; per-fork arrays of the number of open segments
   * (md_num_open_segs) and the segments themselves (md_seg_fds).
   */
  int      md_num_open_segs[MAX_FORKNUM + 1];
  struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];

  /* if unowned, list link in list of all unowned SMgrRelations */
  dlist_node  node;
} SMgrRelationData;

外部调用流程如下:
1)外部buffer pool调用smgrread获取数据
2)smgr调用smgrrsw去获取
3)smgrsw调用底层MD接口,具体接口使用函数表定义


static const f_smgr smgrsw[] = {
    /* magnetic disk */
    {mdinit, NULL, mdclose, mdcreate, mdexists, mdunlink, mdextend,
        mdprefetch, mdread, mdwrite, mdwriteback, mdnblocks, mdtruncate,
        mdimmedsync, mdpreckpt, mdsync, mdpostckpt
    }
};

具体代码文件:smgr.c

6.磁盘管理器(MD)

Md是berkeyley开源的磁盘管理器,可以访问磁盘和ssd,可以看md.c文件。负责磁盘文件的打开,创建,删除。

7.虚拟文件管理器(VFD)

虚拟文件管理器的作用是为了防止句柄数量超过操作系统的限制,如果超过就需要进行淘汰。采用的是LRU的淘汰机制,可以看fd.c。

8.物理文件存储介绍

在这里和MySQL InnoDB做个比较,InnoDB采用的是段页式管理,而PG采用的只有8k分页,代码上来说更为简洁。

MySQL InnoDB:
在这里插入图片描述
PG是按8k分页,可以看到其实是将变与不变部分进行了分离,item为大小不变部分(也就是单个记录的元数据),tuple是大小可变部分。
在这里插入图片描述


//Header信息如下:
typedef struct PageHeaderData
{
    /* XXX LSN is member of *any* block, not only page-organized ones */
    PageXLogRecPtr pd_lsn;      /* LSN: next byte after last byte of xlog
                                 * record for last change to this page */
    uint16      pd_checksum;    /* checksum */
    uint16      pd_flags;       /* flag bits, see below */
    LocationIndex pd_lower;     /* offset to start of free space */
    LocationIndex pd_upper;     /* offset to end of free space */
    LocationIndex pd_special;   /* offset to start of special space */
    uint16      pd_pagesize_version;
    TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
    ItemIdData  pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;

typedef PageHeaderData *PageHeader;

//ItemData信息如下:
typedef struct ItemIdData
{
    unsigned    lp_off:15,      /* offset to tuple (from start of page) */
                lp_flags:2,     /* state of item pointer, see below */
                lp_len:15;      /* byte length of tuple */
} ItemIdData;

typedef ItemIdData *ItemId;

9. 总结

本篇介绍了元组获取的五个层级,从发出获取元组的请求,依次经过Buffer(真正的页数据建立起的映射),存储管理器(SMGR,支持扩展对接不同的存储介质,目前只有磁盘),磁盘管理器(MD,真正访问磁盘,read,write操作),虚拟文件管理器(VFD,用以防止文件打开数目超过限制),最终获得磁盘的页。

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

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

相关文章

【攻防世界新手入门】simple_js

小宁发现了一个网页,但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 该题来自攻防世界新手练习区的 GFSJ0480。 访问靶场,输入任意密码,弹出错误窗口并加载空页面,使用检查器查看对应源代码&…

zdppy+vue3+onlyoffice文档管理系统实战 20240901 上课笔记 基于验证码登录功能基本完成

遗留的问题 1、点击切换验证码2、1分钟后自动切换验证码 点击切换验证码 实现步骤&#xff1a; 1、点击事件2、调用验证码接口3、更新验证码的值 点击事件 给图片添加点击事件&#xff1a; <img :src" data:image/png;base64, captchaImg"style"widt…

ffplay源码分析(五)包缓存队列和帧缓存队列

在音视频处理流程中&#xff0c;ffplay的有两种队列&#xff0c;包缓存队列&#xff08;Packet Buffer Queue&#xff09;和帧缓存队列&#xff08;Frame Buffer Queue&#xff09;。这两个队列的存在&#xff0c;是为了适应音视频数据处理过程中的多线程架构——包括收包线程、…

win11+vscode+Flutter 开发环境配置

https://blog.csdn.net/Oven_maizi/article/details/126804404 1 vscode插件 安装 安装红框中的两个 2 flutter sdk 安装 dart sdk 包含在flutter sdk 里面&#xff0c;路径&#xff1a;flutter_windows_3.24.1-stable\flutter\bin\cache\dart-sdk 方式1&#xff1a; 通过…

[001-07-001].Redis7缓存双写一致性之更新策略探讨

1、面试题&#xff1a; 1.只要使用缓存&#xff0c;就可能会涉及到redis缓存与数据库双存储双写&#xff0c;只要是双写&#xff0c;就存在数据一致性问题&#xff0c;那么是如何解决数据一致性问题的2.双写一致性&#xff0c;你先动缓存redis还是数据库MySQL&#xff0c;哪一个…

Python爬虫案例四:爬取某个博主的所有文章保存成PDF格式

引入&#xff08;将图片保存成PDF格式&#xff09;&#xff1a; 测试链接&#xff1a; https://zq.bookan.com.cn/?tdetail&id21088&ct1&is31042341&rid4658&#xff08;图书馆图片保存PDF&#xff09;&#xff0c;前提是装库&#xff0c;pip install img2pdf…

IDEA如何将某个文件夹内的多个module合并到一个大的module内

效果&#xff1a; 初始 方法 Ctrl shift Alt S 打开 project structure 导入module 将这个大文件夹作为新的module导入 效果图 完事儿

C#上位机采用数据库操作方式对Excel或WPS表格进行读取操作

C#采用数据库操作方式对Excel或WPS表格进行读取操作 1、创建连接字符串并编写一个进行数据库操作的方法 public class OleDbHelper{//创建连接字符串private static string connString "ProviderMicrosoft.ACE.OLEDB.12.0;Data Source{0};" "Extended Propert…

如何在态势中嵌入感知?

态势感知是对事物发展的各种情况的估计、判断和理解&#xff0c;而感知是通过感官对事物的认识和了解。要在态势过程中嵌入感知过程&#xff0c;你可以考虑以下步骤&#xff1a; 1、确定态势和感知的要素&#xff1a;首先&#xff0c;明确态势和感知所涉及的关键要素。态势可能…

Lesson08---string类(1)

1. 为什么要学习string类 2. 标准库中的string类 3. string类的模拟实现 4. 扩展阅读 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 在c语言中没有string类型只有char类型&#xff0c;char类型只能是一个字符非常的不好用&#xff0c;想要字符串还必须要弄一个c…

华溢艺术之花在社区绽放

华溢艺术之花在社区绽放 这是一个阳光明媚的周末&#xff0c;华溢艺术少儿艺术体验走进了夷陵区营盘社区碧桂园小区。这次活动不仅为社区的孩子们带来了丰富多彩的艺术体验&#xff0c;还让家长们目睹了孩子们在艺术成长道路上那些优美动人的闪光点。 活动现场&#xff0c;热闹…

线性代数|机器学习-P32循环矩阵的特征向量-傅里叶矩阵

文章目录 1. 大纲2. 循环矩阵2.1 移位矩阵P2.2 P的特征值和特征向量2.3 循环卷积矩阵2.4 循环卷积计算 3. 傅里叶矩阵 1. 大纲 循环矩阵在机器学习&#xff0c;图像处理中的应用循环卷积矩阵的特征值&#xff0c;特征向量&#xff0c;卷积规则循环卷积矩阵多项式表达&#xff…

kaggle平台free使用GPU

1、注册 请保证在【科学上网】条件下进入如下操作&#xff0c;只有在注册账户和手机号验证时需要。 step1&#xff1a;注册账户 进入kaggle官网&#xff1a;https://www.kaggle.com/&#xff0c;点击右上角【Register】进入注册页面 最好选择使用邮箱注册&#xff08;&#…

零域(微隔离)详述

一、什么是零信任微隔离&#xff1f; 微隔离的实现方式是将数据中心内部所有的业务按照特定的原则划分为数个微小的网络节点&#xff0c;根据动态策略分析对这些节点执行访问控制&#xff0c;在逻辑上将这些节点隔离开&#xff0c;限制用户横向移动&#xff0c;这就是微…

docker-compose 快速部署nacos-standalone单节点

一、nacos 介绍 官网&#xff1a; https://nacos.io/ 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台 二、如何使用docker-compose 快速部署nacos2.3.1 ⚠️ &#xff1a; nacos-standalone 部署方式 依赖于 数据库&#xff0c;请先配置好数据库实例&…

日系编曲:了解日系音乐 日系和声特征 设计日系和声 和弦进行摘抄

了解日系音乐 日系音乐风格多样&#xff0c;涵盖流行、摇滚、民谣、古典等多种类型。以下是部分知名的日系音乐作品、歌手及乐队&#xff1a; 作品 《First Love》是宇多田光的代表作之一&#xff0c;旋律悠扬&#xff0c;情感真挚&#xff0c;展现了初恋的美好与青涩&#xf…

c++ 原型模式

文章目录 什么是原型模式为什么要使用原型模式使用场景示例 什么是原型模式 用原型实例指定创建对象的种类&#xff0c;并通过拷贝这些原型创建新的对象&#xff0c;简单理解就是“克隆指定对象” 为什么要使用原型模式 原型模式&#xff08;Prototype Pattern&#xff09;是…

C++学习笔记----6、内存管理(一)---- 使用动态内存(1)

当你使用现代结构&#xff0c;例如std::vector,std::string等等&#xff0c;从一开始到现在以及到未来&#xff0c;C是一个安全的编程语言。该语言提供了许多的道路&#xff0c;路线以及红绿灯&#xff0c;比如C核心指导&#xff0c;静态代码分析器来分析代码的正确性&#xff…

SQLite3 数据库

一、SQLite3 命令 1. 基本命令 .tables&#xff1a;用于查看数据库中的表。.headers on/off&#xff1a;开启或者关闭表头&#xff0c;方便查看表结构。.mode column&#xff1a;使列对齐&#xff0c;增强数据的可读性。.width 列宽1 列宽2&#xff1a;设置每一列的列宽&…

大量图片进行自适应处理

首先我们手里有一个视频&#xff0c;但是我们对视频进行图像处理的话视频很难暂停进行观察&#xff0c;我们可以写一个按键坚挺&#xff0c;但是小编这里介绍另一个办法&#xff0c;大致思路为&#xff0c;把视频进行截帧为图片的集合&#xff0c;再对该集合进行统一的图像处理…