百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

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

liebian365 2024-11-05 11:45 25 浏览 0 评论

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)

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

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

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

8.物理文件存储介绍

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

MySQL InnoDB:

7


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,用以防止文件打开数目超过限制),最终获得磁盘的页。

相关推荐

go语言也可以做gui,go-fltk让你做出c++级别的桌面应用

大家都知道go语言生态并没有什么好的gui开发框架,“能用”的一个手就能数的清,好用的就更是少之又少。今天为大家推荐一个go的gui库go-fltk。它是通过cgo调用了c++的fltk库,性能非常高...

旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗

这几天老毛桃整理了几个微型Linux发行版,准备分享给大家。要知道可供我们日常使用的Linux发行版有很多,但其中的一些发行版经常会被大家忽视。其实这些微型Linux发行版是一种非常强大的创新:在一台...

codeblocks和VS2019下的fltk使用中文

在fltk中用中文有点问题。英文是这样。中文就成这个样子了。我查了查资料,说用UTF-8编码就行了。edit->Fileencoding->UTF-8然后保存文件。看下下边的编码指示确...

FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库

FLTK(FastLightToolkit)是一个轻量级的跨平台GUI库,特别适用于开发需要快速、高效且简单界面的应用程序。本文将介绍Python中的FLTK库,包括其特性、应用场景以及如何通过代...

中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux

IT之家1月29日消息,去年6月份,中科院大学教授、中科院计算所研究员包云岗,发布了开源高性能RISC-V处理器核心——香山。近日,包云岗在社交平台晒出图片,香山芯片已流片,回片后...

Linux 5.13内核有望合并对苹果M1处理器支持的初步代码

预计Linux5.13将初步支持苹果SiliconM1处理器,不过完整的支持工作可能还需要几年时间才能完全完成。虽然Linux已经可以在苹果SiliconM1上运行,但这需要通过一系列的补丁才能...

Ubuntu系统下COM口测试教程(ubuntu port)

1、在待测试的板上下载minicom,下载minicom有两种方法:方法一:在Ubuntu软件中心里面搜索下载方法二:按“Ctrl+Alt+T”打开终端,打开终端后输入“sudosu”回车;在下...

湖北嵌入式软件工程师培训怎么选,让自己脱颖而出

很多年轻人毕业即失业、面试总是不如意、薪酬不满意、在家躺平。“就业难”该如何应对,参加培训是否能改变自己的职业走向,在湖北,有哪些嵌入式软件工程师培训怎么选值得推荐?粤嵌科技在嵌入式培训领域有十几年经...

新阁上位机开发---10年工程师的Modbus总结

前言我算了一下,今年是我跟Modbus相识的第10年,从最开始的简单应用到协议了解,从协议开发到协议讲解,这个陪伴了10年的协议,它一直没变,变的只是我对它的理解和认识。我一直认为Modbus协议的存...

创建你的第一个可运行的嵌入式Linux系统-5

@ZHangZMo在MicrochipBuildroot中配置QT5选择Graphic配置文件增加QT5的配置修改根文件系统支持QT5修改output/target/etc/profile配置文件...

如何在Linux下给zigbee CC2530实现上位机

0、前言网友提问如下:粉丝提问项目框架汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命...

Python实现串口助手 - 03串口功能实现

 串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。一、...

为什么选择UART(串口)作为调试接口,而不是I2C、SPI等其他接口

UART(通用异步收发传输器)通常被选作调试接口有以下几个原因:简单性:协议简单:UART的协议非常简单,只需设置波特率、数据位、停止位和校验位就可以进行通信。相比之下,I2C和SPI需要处理更多的通...

同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理

串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.对于软件而言,因为驱动接口固定,软件也相对好写,因...

嵌入式linux为什么可以通过PC上的串口去执行命令?

1、uboot(负责初始化基本硬bai件,如串口,网卡,usb口等,然du后引导系统zhi运行)2、linux系统(真正的操作系统)3、你的应用程序(基于操作系统的软件应用)当你开发板上电时,u...

取消回复欢迎 发表评论: