前言
数据库集簇是数据库的集合,数据库是数据库对象的集合,而数据库对象是用来存储或引用数据库的数据结构。而数据表就是其中一种。还有索引、序列、视图、函数等。
数据库集簇逻辑结构
由图可清晰看到数据库集簇是由数据库组成的,而单个数据库又是由许多数据库对象如数据表,索引或者其他数据库对象如视图,函数,序列等等构成的。
而所有的数据库对象都由各自的对象标识符(OID)在内部管理,这些对象标识符是无符号的4字节整数。数据库对象和相应 OID 之间的关系存储在相应的系统目录种,具体取决于对象的类型。
数据库和数据表的OID分别存储在pg_database和pg_class中,查询得:
select datname, oid from pg\_database order by oid;
select relname, oid from pg\_class order by oid;
数据库集簇物理结构
数据库集簇物理结构上来看就是一个基目录,而它的路径通常设置为环境变量PGDATA,在本人的机器上这个PGDATA 为 /data 而数据库则是基目录下的一个子目录,而数据库中的数据表,索引等等都是存储在数据库的目录下的子目录的文件中。(PostgreSQL中表空间是一个包含在基目录之外的一些数据的目录)
数据库集簇的布局
数据库集群使用的配置和数据文件一起存储在集群的数据目录中,这个目录在本机的地址是/data。它包含了多个子目录和控制文件
基目录的(部分)内容:
文件 | 描述(它里面包含的内容) |
---|---|
PG_VERSION | 包含 PostgreSQL 主要版本号文件 |
base | 包含了每个数据库子目录 |
global | 包含集群范围表,如pg_database |
current_logfiles | 记录日志记录收集器当前写入日志文件 |
pg_commit_ts | 包含事务提交时间戳数据 |
pg_dynshmem | 包含动态共享内存子系统使用文件 |
pg_logical | 包含用于逻辑解码的状态数据 |
pg_notify | 包含侦听/通知状态数据 |
详见:基目录的内容
数据库布局
数据库是base子目录下的子目录,数据库目录名称与相应的OID相同。
查找数据库对应的oid:
然后去到data/base目录下,查看子目录名称
发现子目录名称与OID确实一一对应
问:pgsql_tmp是什么文件为什么在base目录里面
他是一个临时文件目录,临时文件会放在这里面,如排序,哈希产生的临时文件
与表和索引关联的文件布局
每个大小小于1GB的表或索引都是存储在所属数据库目录下的单个文件。作为数据库对象的表和索引由各个OID在内部管理,数据文件由变量relfilenode管理。
注意:表和索引的相对节点值基本但并不总是与相应的OID匹配
创建一个表名为t的表,查看它的oid和relfilenode:
查找表t的数据文件路径
表和索引的relfilenode值通过发出命令(如:截断,重新索引,cluster)等来更改
截断表:
truncate t;
通过内置函数找到指定OID或者名称的文件路径名
pg_relation_filepath('tablename')
往表t中插入数据,使表t的内存>1G,观察存储在所属数据库目录下的文件
查看表t所占内存
确实占了3G<X<4G需要用到4张表。
配置选项–with-segsize更改表和索引的最大文件大小
查看数据库子目录
一开始发现只有一个文件
插入数据后,发现除了relfilenode文件外,还多了后缀为"_fsm"的文件
当我对表进行修改后
当我删除表,drop table t后
等再次查看
"_fsm"为可用空间映射(Free Space Map)文件,FSM文件并不是在创建表文件时就创建的,而是等到需要时才会创建,也就是执行为了插入行而第一次查询FSM文件时才创建。
"_vm"为可见性映射文件,同样的也不是创建表文件时就创建的它为每个数据块设置了一个标志位,用来标记数据块中是否存在需要清理的行,update后出现的。同时我们发现数据所占空间多了,是因为更新或者删除某行后,改行并不会马上从数据块中清楚,而是要等待执行命令VACUUM时再清理。
表空间
表空间是基目录之外的附加数据区域。
发出 CREATE TABLESPACE 语句时指定的目录下创建一个表空间
在/home/omm/tblspc中创建一个表空间new_tblspc
那么就会在表空间下创建一个子目录
表空间目录由pg_tblspc子目录中符号链接寻址,并且链接名称与表空间的OID值相同
如果在表空间下 创建新数据库
查看它的oid
查看这个"PG_9.2_201611171_db1"的子目录
发现如果在表空间下创建新数据库,那么将在特定于版本的子目录下创建其目录。
同样的如果在new_tblspc表空间内创建一个新表,但新表所属的数据库却创建在基础目录下,那么PostgreSQL会在new_tblspc表空间内特定于版本的子目录下,创建目录名称于现有数据库oid相同的子目录,然后将新表文件放置在这个目录下。
创建新数据表
查找新数据表所属地址(发现是在所属数据库是创建在基础目录下)
堆表文件的内部布局
表文件是由许多个页(区块)组成,而每一页(区块)被划分成固定长度(默认存储空间为8KB)。每个页都从0开始按顺序编号,这类编号叫做块编号。当文件填满时,PostgreSQL会在文件末尾添加一个新的空页面以增加文件大小。页面的内部布局取决于数据文件类型
堆表文件的页面布局如下:
页会有三种数据
-
堆元组——记录数据,从 页面底部 开始依次堆叠。
-
行指针(作为索引)——行指针长度为4B,并包含指向每个堆元组的指针,这一堆指向堆元组的指针形成一个简单的数组作为元组索引,每个索引项从1开始编号,是偏移号。当页面中添加新元组时,一个相应的新指针就放进行元组索引数组中,指向新元组
-
头数据——放在页面的开头,长度为24B,放一些常规信息,包括:
1. pd\_lsn 页面最后一次更改写入xlog记录的lsn。 2. pd\_checksum存储页面校验和值 3. pd\_lower指向行指针末尾 4. pd\_upper指向最新堆元组的开头 5. pd\_special用于所以,在表中的页面中,它指向页面末尾(在索引中的页面中,它指向特殊空间的开头)
编写和读取元组的方法
编写堆元组
如图所示:
当页面只有一个块的时候pd_lower指向行指针(图中1号指针),行指针和pd_upper指向该块,当要插入新块时,pd_lower指向新的行指针(图中是2号指针),1号指针仍然指向一号块,而pd_upper和新的行指针指向新的块(2号块),2号块放在1号块上面,当还要插入新块时,pd_lower再次指向新的行指针,原本的指针仍然指向原本各自指向的块,pd_upper和新指针指向新的块,新的块又再次堆在上一块的上面,以此类推。
读取堆元组
顺序扫描
字面意思理解,通过扫描每一页中的所有行指针,按顺序读取所有页面中的所有元组一一读取。
B树索引扫描
索引文件包含索引元组,索引元组由一个键值对组成,键为被索引的列值。值是包含着所要查询的元组的所在区块号和偏移量。根据找到所要访问元组的索引元组,根据它的TID内容,去到第n页然后再通过偏移量移动到该堆元组即可。(快捷)