PostgreSQL数据库存储结构

PostgreSQL数据库存储结构

  • 数据组织结构
    • [数据库 Database](#数据库 Database)
    • [System catalog](#System catalog)
    • [模式 Schema](#模式 Schema)
    • [表空间 Tablespace](#表空间 Tablespace)
    • [关系 Relation](#关系 Relation)
    • [分支 Forks](#分支 Forks)
    • [数据页 Page](#数据页 Page)
    • TOAST
  • 数据文件结构
  • 文件系统布局
    • [数据目录:base, global, pg_tblspc, pg_location](#数据目录:base, global, pg_tblspc, pg_location)
    • [日志目录:pg_clog, pg_xact, pg_wal, pg_xlog](#日志目录:pg_clog, pg_xact, pg_wal, pg_xlog)

数据组织结构

数据库 Database

与MySQL类似,每台PG Server上运行的实例可以管理多个Database。这些Database的集合被称为一个Database Cluster。

每个PG实例初始化后会自动创建三个初始数据库:template0、template1和postgres。

  • template0:用于逻辑备份恢复和创建特殊编码的数据库。template0不能被修改。
  • template1:创建其他用户数据库的模板。
  • postgres:默认创建的一个常规数据库。

System catalog

System catalog中存放了数据库元数据相关的系统表。这些表命名都以pg_开头。

从所有数据库中都可以访问这些系统表。

模式 Schema

模式是所有数据库对象的一个逻辑集合。PG数据库中的Schema与MySQL中的类似。每个Schema对应一个数据库。每个数据库对象都有自己所属的模式。

默认的模式是public。所有元数据表所属的模式是pg_catalog

表空间 Tablespace

PG数据库中的表空间是指存储数据库数据的文件系统目录。

多个数据库可以共用同一个表空间。每个数据库的数据也可以存储到多个不同的表空间中。

PG数据库初始化时,会自动创建两个表空间:pg_defaultpg_global

  • pg_default:对应$PGDATA/base目录。默认使用的用户表空间。
  • pg_global:对应$PGDATA/global目录。System catalog中所有元数据系统表存放的表空间。

PG数据库中创建自定义表空间时需要指定文件系统目录路径,创建成功后会自动在$PGDATA/pg_tblspc下生成链接文件。

关系 Relation

关系(relation)在PG数据库中是对表、索引、普通视图、物化视图、序列等所有数据库对象的统称。

分支 Forks

关系(relation)的所有信息都存储在分支(Fork)中。分支(Fork)是磁盘上的一个或多个文件。

有以下几种分支(Fork)类型:

  • the main fork :主分支存储了实际的表行数据或者索引行数据。除了视图以外所有类型的关系都有主分支。主分支的文件名以数字编号表示,记录在pg_class系统表的relfilenode字段中。
  • the initialization fork :初始化分支仅对不记录WAL日志的关系生效,例如以CREATE UNLOGGED TABLE语句创建的表(这种表在数据库故障时会丢失数据,因为没有日志用来恢复)。初始化分支的文件名以数字编号_init 表示,其中的数字编号记录在pg_class系统表的relfilenode字段中。
  • free space map :空闲空间映射用于跟踪数据页中的的剩余可用空间。空闲空间映射的文件名以数字编号_fsm 表示,其中的数字编号记录在pg_class系统表的relfilenode字段中。
  • visibility map :可见性映射可以快速展示某个数据页是否需要被VACUUM或者FROZEN。可见性映射的文件名以数字编号_vm 表示,其中的数字编号记录在pg_class系统表的relfilenode字段中。

如果某个分支文件的大小超过了1G,就会自动生成新的分支文件。假设第一个文件名为FILEID_fsm,新文件命名方式依次为FILEID_fsm.1、FILEID_fsm.2、FILEID_fsm.3,以此类推。

示例:

bash 复制代码
tpcc=> select datname,oid from pg_database;
  datname  |  oid  
-----------+-------
 template1 |     1
 template0 | 12810
 postgres  | 12815
 tpcc      | 17153
(4 rows)

tpcc=> select relname,oid,relfilenode from pg_class where relname='bmsql_customer';
    relname     |  oid  | relfilenode 
----------------+-------+-------------
 bmsql_customer | 17165 |       17165
(1 row)

[omm@dbhost PG_9.2_201611171_dn_6001]$ cd /data/cluster/data/dn/dn_6001/pg_tblspc/17152/PG_9.2_201611171_dn_6001
[omm@dbhost PG_9.2_201611171_dn_6001]$ ll -h
total 32K
drwx------ 2 omm omm 16K Sep 20 17:05 17153
drwx------ 2 omm omm   6 Sep 20 16:57 pgsql_tmp

[omm@dbhost PG_9.2_201611171_dn_6001]$ ll -h 17153/17165*
-rw------- 1 omm omm 1.0G Sep 20 17:07 17153/17165
-rw------- 1 omm omm 1.0G Sep 20 17:08 17153/17165.1
-rw------- 1 omm omm 1.0G Sep 20 17:09 17153/17165.2
-rw------- 1 omm omm 406M Sep 20 17:09 17153/17165.3
-rw------- 1 omm omm 896K Sep 20 17:09 17153/17165_fsm
-rw------- 1 omm omm  56K Sep 20 17:09 17153/17165_vm

数据页 Page

数据页(Page)即数据块(Block),是PG数据库中读写操作的最小IO单位。每个数据页默认大小为8K

TOAST

行数据必须存储在单个数据页中。如果行数据太长单个数据页放不下,就可以把长字段存放到一个TOAST表中(类似于将吐司切片),或者将长字段进行压缩。

PG数据库中的TOAST机制支持以下四种策略:

  • plain:不使用TOAST策略。
  • extended:压缩长字段、并把长字段存放到另外的TOAST表中。
  • external:不压缩长字段,仅把长字段存放到另外的TOAST表中。
  • main:先压缩长字段。如果压缩后仍然放不到单个数据页中,再把长字段存放到另外的TOAST表中。

所有的TOAST表都存放在单独的pg_toast模式下。

sql 复制代码
tpcc=> select attname,atttypid::regtype,
  case attstorage
    when 'p' then 'plain'
    when 'e' then 'extended'
    when 'm' then 'main'
    when 'x' then 'extended'
  end as storage 
from pg_attribute where attrelid='bmsql_customer'::regclass and attnum > 0;

    attname     |          atttypid           | storage  
----------------+-----------------------------+----------
 c_w_id         | integer                     | plain
 c_d_id         | integer                     | plain
 c_id           | integer                     | plain
 c_discount     | numeric                     | main
 c_credit       | character                   | extended
 c_last         | character varying           | extended
 c_first        | character varying           | extended
 c_credit_lim   | numeric                     | main
 c_balance      | numeric                     | main
 c_ytd_payment  | numeric                     | main
 c_payment_cnt  | integer                     | plain
 c_delivery_cnt | integer                     | plain
 c_street_1     | character varying           | extended
 c_street_2     | character varying           | extended
 c_city         | character varying           | extended
 c_state        | character                   | extended
 c_zip          | character                   | extended
 c_phone        | character                   | extended
 c_since        | timestamp without time zone | plain
 c_middle       | character                   | extended
 c_data         | character varying           | extended
(21 rows)

数据文件结构

大小不超过1GB的表或索引都会以单个数据文件的形式存放在base或者自定义表空间路径下。

每个表数据文件由多个固定大小的数据页/块(page/block)组成。每个数据页默认大小为8K。

每个数据页中存储了三种数据:header data、line pointer、heap tuple。

  • header data:占用数据页头部24 bytes空间,存放了pd_lsn、pd_checksum、pd_lower、pd_upper、pd_special等关于当前数据页本身的元数据信息。
  • line pointer:每个line pointer指向同一个数据页中唯一一个heap tuple。每个line pointer占用4 bytes空间。
  • heap tuple (堆元组):heap tuple中存储了用户的行记录数据、以及相关的事务ID信息。每个heap tuple由一个TID(tuple identifier)唯一标识。

Line pointer从数据页的顶部往下新增,Heap tuple从数据页的底部往上新增。两者之间的空洞区域为数据页的剩余可用空间。

文件系统布局

PG数据库使用OID数字标识(Object Identifier)来管理数据库对象。

查询数据库对象的OID信息:

sql 复制代码
tpcc=> select datname,oid from pg_database;
  datname  |  oid  
-----------+-------
 template1 |     1
 template0 | 12810
 postgres  | 12815
 tpcc      | 17153
(4 rows)

tpcc=>  select relname,oid,relfilenode from pg_class where relname='bmsql_customer';
    relname     |  oid  | relfilenode 
----------------+-------+-------------
 bmsql_customer | 17165 |       17165
(1 row)

PG数据目录下的文件一览(这里实际是GaussDB的数据目录,供参考):

bash 复制代码
[omm@dbhost ~]$ ll /data/cluster/data/dn/dn_6001/
total 4984
drwx------ 6 omm omm      58 Sep 20 11:05 base
-rw------- 1 omm omm    1277 Sep 13 10:25 cacert.pem
-rw------- 1 omm omm      72 Sep 20 16:33 gaussdb.state
drwx------ 3 omm omm    4096 Sep 20 17:05 global
-rw------- 1 omm omm     354 Sep 13 10:39 gs_gazelle.conf
-rw------- 1 omm omm 4915200 Sep 13 10:39 gswlm_userinfo.cfg
drwx------ 2 omm omm       6 Sep 13 10:39 hotpatch
-rw------- 1 omm omm      48 Sep 13 10:39 paxosinfo
-rw------- 1 omm omm      48 Sep 13 10:39 paxosinfo.backup
drwx------ 2 omm omm      26 Sep 13 10:39 pg_clog
drwx------ 2 omm omm      26 Sep 13 10:39 pg_csnlog
-rw------- 1 omm omm    1024 Sep 13 10:39 pg_ctl.lock
drwx------ 2 omm omm       6 Sep 13 10:39 pg_errorinfo
-rw------- 1 omm omm    4600 Sep 20 16:31 pg_hba.conf
-rw------- 1 omm omm    4538 Sep 13 10:40 pg_hba.conf.bak
-rw------- 1 omm omm    1024 Sep 13 10:39 pg_hba.conf.lock
-rw------- 1 omm omm    1685 Sep 13 10:40 pg_ident.conf
drwx------ 4 omm omm      39 Sep 13 10:39 pg_llog
drwx------ 3 omm omm      24 Sep 20 11:08 pg_location
drwx------ 2 omm omm      35 Oct 10 11:19 pg_logical
drwx------ 4 omm omm      36 Sep 13 10:39 pg_multixact
drwx------ 2 omm omm      26 Sep 20 16:33 pg_notify
drwx------ 2 omm omm       6 Sep 13 10:39 pg_replslot
drwx------ 2 omm omm       6 Sep 13 10:39 pg_serial
drwx------ 2 omm omm       6 Sep 13 10:39 pg_snapshots
drwx------ 2 omm omm      25 Oct 10 11:19 pg_stat_tmp
drwx------ 2 omm omm      45 Sep 20 13:44 pg_tblspc
drwx------ 2 omm omm       6 Sep 13 10:39 pg_twophase
-rw------- 1 omm omm       4 Sep 13 10:39 PG_VERSION
drwx------ 3 omm omm   16384 Oct  9 19:29 pg_xlog
-rw------- 1 omm omm   35562 Sep 20 16:33 postgresql.conf
-rw------- 1 omm omm   35561 Sep 20 15:10 postgresql.conf.bak
-rw------- 1 omm omm    1024 Sep 13 10:39 postgresql.conf.lock
-rw------- 1 omm omm      87 Sep 20 16:33 postmaster.opts
-rw------- 1 omm omm     107 Sep 20 16:33 postmaster.pid
-rw------- 1 omm omm       0 Sep 13 10:39 postmaster.pid.lock
-rw------- 1 omm omm    4367 Sep 13 10:25 server.crt
-rw------- 1 omm omm    1766 Sep 13 10:25 server.key
-rw------- 1 omm omm      56 Sep 13 10:39 server.key.cipher
-rw------- 1 omm omm      24 Sep 13 10:39 server.key.rand
drwx------ 5 omm omm      67 Sep 13 10:39 undo

数据目录:base, global, pg_tblspc, pg_location

  • base 路径下存放了默认表空间pg_default中的数据文件,不包含创建时指定了自定义路径的用户表空间。
  • global 路径下存放了系统表空间pg_global中的数据文件,主要是pg_开头的元数据表文件。
bash 复制代码
[omm@dbhost ~]$ ls /data/cluster/data/dn/dn_6001/base
1  12810  12815  pgsql_tmp
  • pg_tblspc路径下存放的是各个用户表空间的软链接。
bash 复制代码
[omm@dbhost ~]$ ll /data/cluster/data/dn/dn_6001/pg_tblspc/
total 0
lrwxrwxrwx 1 omm omm 57 Sep 20 11:08 17144 -> /data/cluster/data/dn/dn_6001/pg_location/tablespace/tbs1
lrwxrwxrwx 1 omm omm 57 Sep 20 11:13 17145 -> /data/cluster/data/dn/dn_6001/pg_location/tablespace/tbs2
lrwxrwxrwx 1 omm omm 57 Sep 20 13:44 17152 -> /data/cluster/data/dn/dn_6001/pg_location/tablespace/tpcc
lrwxrwxrwx 1 omm omm 23 Oct 11 12:46 17680 -> /home/omm/tblspc/testbs

⭐️ 创建自定义表空间指定路径的两种方法:

gaussdb=> create tablespace tpcc relative location 'tablespace/tpcc';

gaussdb=> create tablespace testbs location '/home/omm/tblspc/testbs';

  • pg_location 路径下存放了创建自定义表空间时通过RELATIVE LOCATION关键字指定的相对子路径。
bash 复制代码
[omm@dbhost ~]$ ls /data/cluster/data/dn/dn_6001/pg_location/tablespace/tpcc
PG_9.2_201611171_dn_6001

[omm@dbhost ~]$ ls /data/cluster/data/dn/dn_6001/pg_location/tablespace/tpcc/PG_9.2_201611171_dn_6001/
17153  pgsql_tmp

[omm@dbhost ~]$ ls /data/cluster/data/dn/dn_6001/pg_location/tablespace/tpcc/PG_9.2_201611171_dn_6001/17153 | grep 17165
17165
17165.1
17165.2
17165.3
17165_fsm
17165_vm

OID=17153对应数据库tpcc,OID=17165对应表bmsql_customer。

⭐️ 自定义表空间路径下会生成一个子目录,命名方式为PG_[Major version]_[Catalogue version number]

函数pg_relation_filepath可用于查询给定名称或OID的相对磁盘路径:

sql 复制代码
tpcc=>  select relname,oid,relfilenode from pg_class where relname='bmsql_customer';
    relname     |  oid  | relfilenode 
----------------+-------+-------------
 bmsql_customer | 17165 |       17165
(1 row)

tpcc=> select pg_relation_filepath(17165);
                 pg_relation_filepath                 
------------------------------------------------------
 pg_tblspc/17152/PG_9.2_201611171_dn_6001/17153/17165
(1 row)

tpcc=> 
tpcc=> select pg_relation_filepath('bmsql_customer');
                 pg_relation_filepath                 
------------------------------------------------------
 pg_tblspc/17152/PG_9.2_201611171_dn_6001/17153/17165
(1 row)

日志目录:pg_clog, pg_xact, pg_wal, pg_xlog

  • pg_clog (版本9.6和9.6之前)和pg_xact(版本10和10之后)路径下存放了事务提交状态的相关日志。
  • pg_xlog (版本9.6和9.6之前)和pg_wal(版本10和10之后)路径下存放了WAL日志文件。

References

[1] https://www.interdb.jp/pg/

[2] https://edu.postgrespro.com/postgresql_internals-14_en.pdf

[3] https://www.postgresql.org/docs/14/storage-file-layout.html

相关推荐
superman超哥20 分钟前
04 深入 Oracle 并发世界:MVCC、锁、闩锁、事务隔离与并发性能优化的探索
数据库·oracle·性能优化·dba
engchina1 小时前
Neo4j 和 Python 初学者指南:如何使用可选关系匹配优化 Cypher 查询
数据库·python·neo4j
engchina1 小时前
使用 Cypher 查询语言在 Neo4j 中查找最短路径
数据库·neo4j
尘浮生1 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
威哥爱编程1 小时前
SQL Server 数据太多如何优化
数据库·sql·sqlserver
小华同学ai1 小时前
AJ-Report:一款开源且非常强大的数据可视化大屏和报表工具
数据库·信息可视化·开源
Acrelhuang2 小时前
安科瑞5G基站直流叠光监控系统-安科瑞黄安南
大数据·数据库·数据仓库·物联网
十叶知秋2 小时前
【jmeter】jmeter的线程组功能的详细介绍
数据库·jmeter·性能测试
瓜牛_gn4 小时前
mysql特性
数据库·mysql
奶糖趣多多5 小时前
Redis知识点
数据库·redis·缓存