PG INTERNAL-Database Cluster, Databases, and Tables

本文深入探讨了PostgreSQL数据库集群的逻辑和物理结构,包括database cluster的目录布局、数据库、表和索引的文件组织,以及heap table文件的内部逻辑。文章详细阐述了tablespace的概念,以及如何读写heap tuple,提供了理解PostgreSQL存储机制的基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译http://www.interdb.jp/pg/pgsql01.html

这一章和下一个章节总结Postgresql的基础知识,帮助读者理解后续章节.这一章,描述了下面的主题:

  • database cluster的逻辑结构
  • database cluster的物理结构
  • heap table文件的内部逻辑布局
  • 的读写数据的方法

1.1 database cluster的逻辑结构

一个database cluster是一组被Postgresql服务器管理的database.如果你是第一次听说这个定义,那么你可能会对它产生好奇,但是database cluster这个术语在Postgresql中不是表示一组database服务器.一个Postgresql服务器运行在一个主机(host)上并且管理着一个database cluster.

下面的图显示了一个database cluster的逻辑结构.一个databasedatabase对象的一个集合.在关系数据库理论中,一个database对象是用于存储或者引用数据的数据结构.一个(heap) table是数据库对象的典型例子,其他的比如index,sequence,view,function等都是数据库对象.在Postgresql中,数据库本身也是数据库对象,在逻辑上是相互分隔的.其他所有数据库对象都属于他们对应的数据库.

在这里插入图片描述

在pg中所有的数据库对象内部都有对应的object identifiers(OIDS).它是4字节无符号整数.数据库对象之间的关系和对应的OIDS都保存在合适的相应的对象类型的system catalog.比如,database和heap table的OIDS保存在pg_databasepg_class中,所以可以通过下面的查询找出你想要的OIDS:

sampledb=# SELECT datname, oid FROM pg_database WHERE datname = 'sampledb';
datname  |  oid  
----------+-------
sampledb | 16384
(1 row)

sampledb=# SELECT relname, oid FROM pg_class WHERE relname = 'sampletbl';
relname  |  oid  
-----------+-------
 sampletbl | 18740 
(1 row)

1.2 database cluster的物理结构

一个database cluster基本上就是作为基础目录(base directory)的一个目录.它包含了许多子目录和文件.如果你执行initdb初始化一个新的database cluster,在指定的目录下一个基础目录就会被创建.虽然不是必须的,但是通常会把基础目录的路径设置为环境变量PGDATA.

下图显示了pg中一个database cluster的例子.一个database是base子目录下的一个子目录.每一个表和index在database子目录中保存在(至少)一个文件.另外,也有其他子目录保存一些特别的数据,和配置文件.由于pg支持tablespace,这表面tablespace这一术语与其他数据库的意思是不一样的.一个tablespace在pg中实际是一个不在base子目录中的目录.

在这里插入图片描述

1.2.1 database cluster的布局

官方文档已经描述了database cluster的布局.下面列出主要的文件和子目录:
在这里插入图片描述
在这里插入图片描述

1.2.2 database的布局

数据库目录是在base子目录下的.database的目录名字与对应的OIDS相等.比如,sampledb的OIDS是16384,那么它的子目录名字也就是16384.
在这里插入图片描述

1.2.3 table和index关联的文件布局

每个大小小于1GB的表和index都保存在对应数据库子目录下的一个单独的文件中.table和index作为数据库对象内部都通过单独的OIDS进行管理,同时这些数据文件也由relfilenode变量管理.table和index的relfilenode的值基本上就是OIDS,但并不总是会相同.
在这里插入图片描述
在这里插入图片描述
table和index的relfilenode的值会被一些命令改变(TRUNCATE,REINDEX,CLUSTER).比如,如果我们truncate表sampletbl.Pg会分配一个新的relfilenode,删除旧的数据文件,创建一个新的.
在这里插入图片描述

在9.0以上,内建的函数pg_relation_filepath可用于根据OID或名字返回关系的文件路径.
在这里插入图片描述

当文件大小超过1GB,PG会创建一个新的文件命名的格式是relfilenode.1.如果新的文件也填满了,后面会继续创建relfilenode.2,一直按照这种方式处理.
在这里插入图片描述

table和index文件的最大文件大小可以,在构建postgresql的时候通过参数--with-segsize改变.

仔细看数据库子目录下的文件,你会发现每个表都有两个关联的文件都分别带有_fsm_vm.它们是作为free space mapvisibility map的引用,分别保存表每个page的free space capacity和可见性的信息.indexes只有free space maps没有visibility map.
在这里插入图片描述

1.2.4 Tablespace

tablespace在PG中是在base子目录外的附加的数据区域.这个功能在8.0中实现.

在这里插入图片描述

使用CREATE TABLESPACE会创建对应的tablespace.在对应的目录下,指定版本的子目录也就创建(比如,PG_9.4_201409291).命名方式如下:

PG _ 'Major version' _ 'Catalogue version number'

比如,如果你创建一个new_tblspc的tablespace,路径为/home/postgres/tblspc,oid是16386,那么子目录PG_9.4_201409291会在tablespace中创建.

在这里插入图片描述
tablespace的目录会被pg_tblspc下的子目录软连接过去,连接的名字等于tablespace的OID.
在这里插入图片描述
如果你在tablespace下创建一个新的database(OID是16387),它的子目录创建在指定版本子目录(version-specific subdirectory)下.
在这里插入图片描述
如果在base目录下的一个已创建的数据库下创建一个新的表,使用tablespace.那么第一步,与已存在的数据库的OID相同名字的新目录会在version specific subdirectory下创建,然后再在该目录下创建表的文件.
在这里插入图片描述

1.3 Heap table file的内部布局

在数据文件内部(heap table和index,free space map与visibility map),都会被分割为固定长度的pages(blocks).pg默认是8KB.每个文件的page安装从0开始的序列编号,这叫做block numbers.如果文件被填满,pg会在文件的结尾添加一个空page,填充剩下的空间.
page的内部布局根据数据文件的类型有所不同.
在这里插入图片描述

表中的page包含一下三种数据:

  • heap tuple:heap tuple记录数据本身.它们是从page底部开始的栈结构.
  • line pointer(s):一个linepointer是4字节长度,保存指向heap tuple的指针,也称为item pointer.
  • header data:header data由数据结构PageHeaderData定义,在page的开始的地方.它是24字节长度,包含了关于page的通用信息.
    在这里插入图片描述
    • pd_lsn:这个变量记录最后修改page的XLOG记录的LSN.是8字节无符号整数,与WAL机制有关.
    • pd_cjecksum:保存整个page的校验码(注意这个变量只存在9.3版本以上,之前的版本这部分数据保存在page的timelineId中).
    • pd_lower,pd_upper:pd_lower指向line pointers的结尾,pd_upper指向最新的heap tuple的开头.
    • pd_special:这个变量用于索引.在table中,它指向page的结尾.(在index的page中,它指向一个特殊空间的开头,这个特殊空间保存不同索引类型的特殊数据,比如BTREE,GiST等是不同的).

在line pointer的结尾与最新tuple的开头之间的空间被应用为free space或者hole.

定位一个table中的tuple,内部使用tuple identifier(TID).一个TID由一对数据组成:page的block number和指向tuple的line pointer的偏移数.

PageHeaderData定义在 src/include/storage/bufpage.h
另外,如果heap tuple的大小大于2kb(1/4 of 8 kb),使用 TOAST (The Oversized-Attribute Storage Technique) 方法保存和管理.具体内容可查看官方文档.

1.4 读写tuple的方法

1.4.1 写heap tuple

假设一个表包含一个page,这个page只有一个heap tuple.pd_lower指向第一个line pointer,这个line ponter和pd_upper都指向第一个tuple.

当第二个tuple插入时候,它被放在第一个的后面.第二个line pointer会被push到第一个后面,然后指向第二个tuple.pd_lower改变,并指向第二个line pointer.pd_upper指向第二个tuple.其他的header data(比如pd_lsn,pd_checksum,pd_flag等)都会改变.
在这里插入图片描述

1.4.2 读Heap tuple

在这里描述两种典型的access method,sequential scan和btree index scan.

  • **sequentialscan: 所有page中的所有tuple都是通过扫描所有line pointers顺序读取的.
  • B-tree index scan:一个index文件包含index tuple,每一个index tuple由index key和指向目标heap tuple的TID.如果相应查找的key的index tuple被找到,pg使用TID读取对应的heap tuple.比如,TID的值为(block=7,offset=2),表明目标的heap tuple是第7个page的第二个heap tuple.所以pg就不需要扫描page来读取对应的heap tuple.

在这里插入图片描述

Postgresql也支持TID-scan,Bitmap-Scan,和Index-Only-Scan.

TID-Scan是直接通过TID访问tuple的方法.比如,查找第0个page的第一个tuple,可以使用下面的查询
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值