Postgresql技术内幕系列-第一章数据库集合、数据库、表的逻辑设计与物理设计

第一章数据库集合、数据库、表

1.1数据库集合(database cluster)逻辑结构

1.2数据库集合(database cluster)的物理结构

1.2.1. 数据库集合(database cluster)的布局

1.2.2. database的物理布局设计

1.2.3. 表和索引的物理布局设计

1.2.4. Tablespaces

1.3. 堆表文件(heap table file)的内部结构

1.4. 从表中读写tuple的方法

1.4.1. 写Heap Tuples

1.4.2. 读Heap Tuples


第一章数据库集合、数据库、表

本章和下一章概述了PostgreSQL的基础知识,以帮助读者阅读后续章节。在本章中主要描述以下主题:

  • 数据库集合(database cluster)的逻辑结构

  • 数据库集合(database cluster)的物理结构

  • 堆表文件(heap table file)的内部结构

  • 从表中读写数据的方法

如果您已经熟悉上述主题,可跳过本章阅读后续章节。

1.1数据库集合(database cluster)逻辑结构

 database cluster是由postgresql server管理的数据库对象的集合,由多个database对象组成。如果你现在第一次听到这个定义,你可能会想到“数据库集群”,但PostgreSQL中的术语“database cluster”并不意味着“由多个数据库服务结点组成的集群”。 一个PostgreSQL 服务结点能够在单个主机上运行并管理一个数据库集合(database cluster),即多个数据库对象。

图1.1描述了一个数据库集合的逻辑结构。一个数据库集合由多个数据库对象组成。在关系数据库理论中,数据库对象是用于存储或引用数据的数据结构,包括表、视图、索引、序列、函数等等。在PostgreSQL中,数据库集合由逻辑上独立的多个数据库对象组成,各数据库对象都有专属于自己的表、索引等等。

                                                                                 图1.1 数据库集合的逻辑结构图

所有数据库对象都有各自的oid(object identifiers),oid是一个无符号的四字节整数。数据库、数据表等相关对象的oid都存放在相关的system catalogs中,比如数据库的oid和表的oid分别存放在pg_database,pg_class表中。因此,可以使用如下方法查找数据库、数据表的oid。

 

1.2数据库集合(database cluster)的物理结构

一个数据库集合对应着一个目录,目录中包含一些子目录和大量数据文件。

在执行initdb的时候会初始化一个新的数据库集合,在指定目录下创建一个基目录(base directory )。一般而言,我们会将这个目录用环境变量$PGDATA来表示。

图1.2描述了PostgreSQL中的一个数据库集合示例。数据库集合中的各个数据库都对应于基目录下的一个子目录。数据库中的表、索引信息存储于子目录中的数据文件中,一个表、索引至少有一个文件与之对应。同时,在base directory中,会生成一些配置文件和几个包含特殊数据的子目录。在postgresql中,tablespace的概念并不同于其他关系型数据库,这里一个tablespace对应的都是一个目录,该目录与base目录平级,存储一些base directory外的数据。

                                                           图1.2 数据库集合(database cluster)的物理结构示例

1.2.1. 数据库集合(database cluster)的布局

数据库集合(database cluster)的布局的详细描述见官方文档. 其中的主要文件和子目录如下:

files

description

PG_VERSION

postgresql主版本号文件

pg_hba.conf

postgresql客户端验证的文件

pg_ident.conf

postgresql用户名映射的文件

postgresql.conf

配置参数文件

postgresql.auto.conf

用于存储在ALTER SYSTEM(版本9.4或更高版本)中设置的配置参数的文件

postmaster.opts

记录服务端上一次启动的命令行选项

subdirectories

description

base/

包含每个数据库子目录的子目录

global/

包含集合范围表的子目录,例如pg_database和pg_control

pg_commit_ts/

包含事务提交时间戳数据的子目录。 9.5版本以后

pg_clog/ (Version 9.6 or earlier)

包含事务提交状态数据的子目录。它在版本10中重命名为pg_xact.  CLOG将在5.4章节中详解。

.

pg_dynshmem/

包含动态共享内存子系统使用的文件的子目录。9.4版本以后

pg_logical/

包含逻辑解码的状态数据的子目录。9.4版本以后

pg_multixact/

包含多事务状态数据的子目录(用于 shared row locks)

pg_notify/

包含LISTEN / NOTIFY状态数据的子目录

pg_repslot/

包含复制槽数据的子目录(9.1版本以后)

pg_serial/

包含有关已提交的序列化事务(9.1版本以后)信息的子目录

pg_snapshots/

包含导出快照的子目录(9.2版本以后)。 PostgreSQL的函数pg_export_snapshot在此子目录中创建快照信息文件

pg_stat/

包含统计子系统永久文件的子目录

pg_stat_tmp/

包含统计子系统临时文件的子目录

pg_subtrans/

包含子事物状态数据的子目录

pg_tblspc/

表空间符号链接目录

pg_twophase/

包含prepare事务的状态文件

pg_wal/ (Version 10 or later)

包含WAL(Write Ahead Logging)段文件的子目录。在版本10中从pg_xlog重命名而来

.

pg_xact/ (Version 10 or later)

包含事务提交状态数据的子目录。在版本10中从pg_clog重命名而来.CLOG将在5.4章节中详解

pg_xlog/ (Version 9.6 or earlier)

包含WAL(Write Ahead Logging)段文件的子目录。在版本10中重命名为pg_wal

1.2.2. database的物理布局设计

每个数据库都会在$PGDATA/base下面生成一个子目录,如下图,都会一一对应。子目录名称与数据库的OID一致。例如,数据库sampledb 的oid为16384,其对应的子目录名称为16384。

1.2.3. 表和索引的物理布局设计

每一个表和索引如果不超过1G大小,都只有一个文件。表和索引也有和数据库一样的OID,另外还有一个relfilenode,这个值不会总是匹配OID,在发生一truncate,reindex,cluster等相关的操作,会发生变化,见如下示例:

可以看到开始oid和relfilenode是一样的,truncate后,relfilenode发生了变化。

如果数据数据文件超过1GB,那么就会新生成一个文件,如下:

注意:表和索引的文件大小的限制可以在编译的时候通过--with-segsize设置

观察数据库子目录发现,每一个表都包含oid_fsm和oid__vm命名的文件。其中oid_fsm对应于free space map,存储数据表文件中每个页的剩余空间,详见Section 5.3.4 。oid__vm对应于visibility map,存储数据表文件中每个页中各tuple的可见性信息,详见Section 6.2

hey may also be internally referred to as the forks of each relation; the free space map is the first fork of the table/index data file (the fork number is 1), the visibility map the second fork of the table's data file (the fork number is 2). The fork number of the data file is 0.

这里需要提一下每个关系表中的forknum的含义。在表或索引文件中, free space map(fsm文件)是第一个fork,其forknum为1。visibility map(vm文件)是第二个fork,其forknum为2。数据文件的forknum为0。

1.2.4. Tablespaces

在PG中,除了base目录,自己新建的tablespace对应的目录都会再pg_tblspc下,如下图 

                                                                    图. 1.3. Tablespace 物理布局

当用户执行CREATE TABLESPACE语句并设置tablespace目录,pg将会在指定目录下创建version-specific子目录,格式为版本号_子目录(如PG_9.4_201409291),命名方法如下:

例如,当用户在 '/home/postgres/tblspc'目录下创建一个名称为'new_tblspc'的tablespace时,生成的tablespace oid为16386,pg将在tablespace对应目录下( '/home/postgres/tblspc',)创建子目录'PG_9.4_201409291' 

在base目录pg_tblspc子目录下,生成16386的符号链接,指向“tablespace oid为16386的'new_tblspc'对应的目录,即'/home/postgres/tblspc'。

如果用户在一个已经创建的数据库(sampledb)中,创建新表,将表的tablespace设置为'new_tblspc',那么pg将在'new_tblspc'对应的version specific子目录下创建一个新的目录,该目录名称为sampledb的oid(16384),同时,在此目录下,创建一个表文件,以表oid命名。

1.3. 堆表文件(heap table file)的内部结构

在表对应的datafile中,被分离为固定大小的page(or block),默认为8KB,这些page在datafile中从0开始计数,如果一个page被填充满,那么就会生成新的page以添加到文件,所以我们看到的datafile会随着表的增大,也在不断增大。

一个堆表数据文件的内部结构设计如下图:

  

一个表的page包括三种类型的数据

1.heap tuple: 存放数据本身,从一个page的末端有序的堆积。

2.line pointer: 一个四字节的行指针,指向每一个heap tuple,也叫item pointer,line pointer是一个简单的数组,索引page的数据文件是从1开始计数,也叫offset number,新的tuple增加到page时,line piniter就在推送到数组中,指向新的tuple。

3.header data:  header data是page生成的时候随之产生的,由pageHeaderData定义结构,24个字节长,包含了page的一般信息,主要结构描述如下:

  • pd_lsn: 存储XLOG最后的改变的这个page的LSN号,是一个8字节的无符号整数,和WAL相关,后续章节会有描述

  • pd_checksum:存储page的校验和

  • pd_lower,pd_upper:  pd_lower指向line pointer的尾部,pd_upper指向最新heap tuple的开头

  • pd_special: 此变量用于索引,在表的page中,它指向page的末尾(在索引的page中,它指向特殊空间的开头)

1.4. 从表中读写tuple的方法

1.4.1. 写Heap Tuples

假设一个数据表只有一个page,这个page里只有一个tuple,page中的pd_lower指向第一个tuple的指针(first line pointer),同时第一个tuple的指针(first line pointer)和pd_upper都指向第一个heap tuple,如图1.5(a)。

当tuple2插入后,2号line pointer指向tuple2的头部,pd_lower指向了2号line pointer的末尾,pd_upper指向了tuple2的头部,如图1.5(b),其他的数据(pg_lsn,pg_checksum, pg_flags等等)也会适当的被重写。第5.3节第9章会详细介绍。                                                                              图1.5 写heap tuple

1.4.2. 读Heap Tuples

有两种方法可以读取Heap Tuples,分别是顺序扫描和B-tree索引扫描:

顺序扫描(Sequential scan) –表中的所有page中的所有tuple通过每个page中的所有line pointer依次读取,如图1.6(a)。

B-tree索引扫描(B-tree index scan) –每个索引文件都包含index tuple,每个index tuple都是由索引键和一个指向目标heap tuple的point构成的TID所构成,如果索引的键值被找到,那么就从index tuple中获取TID的值去找想要的数据。如以下示例:通过索引的键值Queen,在index tuple中找到对应的TID(block=7,Offset=2),这里的意思就是第七个page的第二个tuple.因此PG不需要在page中进行没有必要的扫描,如图1.6(b)。

                                                     图1.6 顺序扫描和索引扫描

关于PG中索引的原理,推荐阅读以下内容:

Indexes in PostgreSQL — 1 

Indexes in PostgreSQL — 2 

Indexes in PostgreSQL — 3 (Hash) 

Indexes in PostgreSQL — 4 (Btree) 

Indexes in PostgreSQL — 5 (GiST) 

Indexes in PostgreSQL — 6 (SP-GiST) 

Indexes in PostgreSQL — 7 (GIN) 

Indexes in PostgreSQL — 9 (BRIN) 

PostgreSQL同样支持TID-Scan, Bitmap-Scan,和Index-Only-Scan。

TID-Scan是一种通过使用待获取tuple 的TID直接访问tuple 的方法。例如,要在表中找到第0个块中的第一个tuple ,请发出以下查询:

Index-Only-Scan详细内容见第7章

译自:

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

 

参考:

https://blog.csdn.net/dazuiba008/article/details/80363430

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值