Postgresql管理系列-第一章 逻辑结构和物理结构

1.数据库的逻辑结构

Database cluster: 由postgresql server管理的数据库的集合,下面由多个database组成

databsase: 由各种数据库对象构成,比如下图中的table,indexes, view,function,sequence...

 

所有数据库对象都有各自的oid(object identifiers),oid是一个无符号的四字节整数,相关对象的oid都存放在相关的system catalog表中,比如数据库的oid和表的oid分别存放在pg_database,pg_class表中。

如下所示:

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)

2.数据库的物理结构

在执行initdb的时候会初始化一个目录,通常我们都会在系统配置相关的环境变量$PGDATA来表示,初始化完成后,会再这个目录生成相关的子目录以及一些文件。在postgresql中,tablespace的概念并不同于其他关系型数据库,这里一个tablespace对应的都是一个目录。如下图就是PG的物理结构

文件和目录相关作用描述

 

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

 

2.1 database的物理布局设计

  每个数据库都会在$PGDATA/base下面生成一个子目录,如下图,都会一一对应。

postgres@db-192-168-101-115-> pwd
/opt/pgdata9.6/pg_root/base
postgres@db-192-168-101-115-> ll
total 1.1M
drwx------ 2 postgres postgres 4.0K Aug 29  2017 1
drwx------ 2 postgres postgres 4.0K Aug 29  2017 13268
drwx------ 2 postgres postgres 976K May 14 11:36 13269
drwx------ 2 postgres postgres  12K May 14 11:36 137745
drwx------ 2 postgres postgres 4.0K Aug 29  2017 137915
drwx------ 2 postgres postgres  20K Aug 29  2017 137943
drwx------ 2 postgres postgres  16K Aug 29  2017 137945
drwx------ 2 postgres postgres  12K Aug 29  2017 137947
drwx------ 2 postgres postgres 4.0K Aug 29  2017 139298
drwx------ 2 postgres postgres  20K May 14 11:36 139312
drwx------ 2 postgres postgres  12K Aug 29  2017 17220
drwx------ 2 postgres postgres 4.0K Aug 29  2017 17289
drwx------ 2 postgres postgres 4.0K Aug 29  2017 177374
drwx------ 2 postgres postgres 4.0K Jul 10  2017 pgsql_tmp
hank=> select datname,oid from pg_database; 
  datname  |  oid   
-----------+--------
 postgres  |  13269
 template1 |      1
 template0 |  13268
 hank      |  16395
 rmt_db    |  17220
 loc_db    |  17289
 pgcluster | 137745
 swrd      | 137915
 scm       | 137943
 hive      | 137945
 oozie     | 137947
 sqoop     | 139298
 hue       | 139312
 zabbix    | 177374
(14 rows)

2.2 表和索引的物理布局设计

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

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

hank=> SELECT relname, oid, relfilenode FROM pg_class WHERE relname = 'a';   
 relname |  oid   | relfilenode 
---------+--------+-------------
 a       | 186884 |      186884

 postgres@db-192-168-101-115-> ll -a /opt/pgdata9.6/pg_root/pg_tblspc/16393/PG_9.6_201608131/16395/186884
-rw------- 1 postgres postgres 1.5M Jan 16 17:11 /opt/pgdata9.6/pg_root/pg_tblspc/16393/PG_9.6_201608131/16395/186884


hank=> truncate table a;
TRUNCATE TABLE
hank=> SELECT relname, oid, relfilenode FROM pg_class WHERE relname = 'a';
 relname |  oid   | relfilenode 
---------+--------+-------------
 a       | 186884 |      187892
(1 row)

hank=> SELECT pg_relation_filepath('a');  
             pg_relation_filepath              
-----------------------------------------------
 pg_tblspc/16393/PG_9.6_201608131/16395/187892

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

 cd $PGDATA
$ ls -la -h base/16384/19427*
-rw------- 1 postgres postgres 1.0G  Apr  21 11:16 data/base/16384/19427
-rw------- 1 postgres postgres  45M  Apr  21 11:20 data/base/16384/19427.1

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

3.表空间物理结构

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

比如新建一个tbs_hank表空间,目录是/data02/pgdata/tbs_hank
create tablespace tbs_hank owner postgres location '/data02/pgdata/tbs_hank'; 
那么就会再pg_tblspc下面有一个软连接的目录,这里16393就是这个tablespace的OID
postgres@db-192-168-101-115-> pwd
/opt/pgdata9.6/pg_root/pg_tblspc
postgres@db-192-168-101-115-> ll
total 0
lrwxrwxrwx 1 postgres postgres 23 Oct 10  2016 16393 -> /data02/pgdata/tbs_hank

再往下,就可以看到16395,是database的OID
postgres@db-192-168-101-115-> pwd
/opt/pgdata9.6/pg_root/pg_tblspc/16393/PG_9.6_201608131
postgres@db-192-168-101-115-> ls
16395  pgsql_tmp

4.堆表文件的内部结构

在表对应的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中,它指向特殊空间的开头)

5.读写tuple的方法

5.1写head tuple

假设我们我们的表只有一个page,这个page里只有一个tuple,如下图,pd_lower指向line pointer尾部,pd_upper指向tuple1的头部,当tuple2插入后,2号line pointer指向tuple2的头部,pd_lower指向了2号line pointer的末尾,pd_upper指向了tuple2的头部,其他的数据(pg_lsn,pg_flags等等)也会适当的被重写。后面章节会详解

5.2 读heap tuple

两种典型的访问方法,顺序扫描和B-tree索引扫描

a. sequential  scan:表中的所有page中的所有tuple通过每个page中的所有line pointer依次读取

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

索引内部原理

这一章节不介绍索引,为了明白索引工作原理,建议阅读以下帖子:


PostgreSQL 同样也支持TID-Scan, Bitmap-Scan, and Index-Only-Scan.

TID-Scan is a method that accesses a tuple directly by using TID of the desired tuple. For example, to find the 1st tuple in the 0-th page within the table, issue the following query:

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

sampledb=# SELECT ctid, data FROM sampletbl WHERE ctid = '(0,1)';
 ctid  |   data    
-------+-----------
 (0,1) | AAAAAAAAA
(1 row)

Index-Only-Scan将在 Chapter 7介绍.

 

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

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值