最近看到一本书,相对于官方文档,更加简洁易读一些,这里把一些重点整理成相关文章,以便更多人了解学习
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 | A file containing the major version number of PostgreSQL |
pg_hba.conf | A file to control PosgreSQL's client authentication |
pg_ident.conf | A file to control PostgreSQL's user name mapping |
postgresql.conf | A file to set configuration parameters |
postgresql.auto.conf | A file used for storing configuration parameters that are set in ALTER SYSTEM (version 9.4 or later) |
postmaster.opts | A file recording the command line options the server was last started with |
subdirectories | description |
base/ | Subdirectory containing per-database subdirectories. |
global/ | Subdirectory containing cluster-wide tables, such as pg_database and pg_control. |
pg_commit_ts/ | Subdirectory containing transaction commit timestamp data. Version 9.5 or later. |
pg_clog/ (Version 9.6 or earlier) | Subdirectory containing transaction commit state data. It is renamed to pg_xact in Version 10. CLOG will be described in Section 5.4. |
pg_dynshmem/ | Subdirectory containing files used by the dynamic shared memory subsystem. Version 9.4 or later. |
pg_logical/ | Subdirectory containing status data for logical decoding. Version 9.4 or later. |
pg_multixact/ | Subdirectory containing multitransaction status data (used for shared row locks) |
pg_notify/ | Subdirectory containing LISTEN/NOTIFY status data |
pg_repslot/ | Subdirectory containing replication slot data. Version 9.4 or later. |
pg_serial/ | Subdirectory containing information about committed serializable transactions (version 9.1 or later) |
pg_snapshots/ | Subdirectory containing exported snapshots (version 9.2 or later). The PostgreSQL's function pg_export_snapshot creates a snapshot information file in this subdirectory. |
pg_stat/ | Subdirectory containing permanent files for the statistics subsystem. |
pg_stat_tmp/ | Subdirectory containing temporary files for the statistics subsystem. |
pg_subtrans/ | Subdirectory containing subtransaction status data |
pg_tblspc/ | Subdirectory containing symbolic links to tablespaces |
pg_twophase/ | Subdirectory containing state files for prepared transactions |
pg_wal/ (Version 10 or later) | Subdirectory containing WAL (Write Ahead Logging) segment files. It is renamed from pg_xlog in Version 10. |
pg_xact/ (Version 10 or later) | Subdirectory containing transaction commit state data. It is renamed from pg_clog in Version 10. CLOG will be described in Section 5.4. |
pg_xlog/ (Version 9.6 or earlier) | Subdirectory containing WAL (Write Ahead Logging) segment files. It is renamed to pg_wal in Version 10. |
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中进行没有必要的扫描。