导语
PostgreSQL 数据库是功能强大的开源数据库,越来越多的公司开始应用 PostgreSQL。存储系统是 PostgreSQL 的最底层模块,它向下通过操作系统接口拜访物理数据,向上为下层模块提供存储操作的接口和函数。本文通过对 PostgreSQL 的存储构造进行浅析,帮忙大家理解这一弱小的关系型数据库是如何存储数据的。
– 数据目录 –
PostgreSQL 装置实现后必须先应用 initdb 程序初始化磁盘上的数据存储区,生成模板数据库和相应的目录、文件信息。
initdb -D /usr/local/pgsql/data
初始化目录中蕴含数据文件、参数文件、管制文件、数据库运行日志及 WAL 日志文件等,下图各目录和子文件的用处在此不做赘述。
默认状况下,PostgreSQL 中的所有数据都存储在其数据目录里,这个数据目录通常会用环境变量 PGDATA 来援用,对于某个具体的数据库,在 PGDATA/base 里都对应有一个子目录,子目录的名字是该数据库在零碎表 pg_database 里的 OID,每个表的数据都存在其所属数据库目录下的独立文件里,文件以该表的 filenode 号命名,为了防止有些文件系统不反对大文件,PostgreSQL 限度表文件大小不能超过 1GB(默认 1GB,编译时可通过./configure –with-segsize= x 批改)因而,当表文件超过 1GB 时,会另建一个有尾缀的文件 relfilenode.1,relfilenode.2…… 以此类推。
表文件的物理地位为:$PGDATA/BASE/DATABASE_OID/PG_CLASS.RELFILENODE
— 查看数据库 test 的 OID
#select oid,datname from pg_database where datname='test'
— 查看表 t1 的 filenode
#select pg_relation_filepath('t1')
;
– 数据文件构造 –
在 PG 中,磁盘存储和内存中的最小治理单位都是块,保留在磁盘中的数据块称为 Page,内存中的数据块称为 Buffer,表和索引称为 Relation,行称为 Tuple。数据的读写是以 Page 为最小单位,每个 Page 默认大小为 8kB,在源码编译时可通过./configure –with-blocksize=BLOCKSIZE 设置其余大小,尔后都不可更改。每个表文件由多个 BLCKSZ 字节大小的 Page 组成,每个 Page 蕴含若干 Tuple。
内存中的共享缓冲池缓存了 block 块(默认 1000 个),若缓冲池中的 block 块为脏,须要刷回磁盘,缓冲池细节在此不做赘述,须要时可另起一文解析。
Page 构造
Page 构造包含五局部:
PageHeaderData – 在 page 头部,24 个字节长度,记录 page 的元数据信息。
pg_lsn,存储 page 最新更改时,WAL 日志的 lsn 信息。
pg_checksum,存储 page 的校验值。
pd_flags,标记位
pg_lower,到闲暇空间结尾的偏移量。
pg_upper,到闲暇空间结尾的偏移量。
pd_pagesize_version,页面大小和布局版本号信息
pd_prune_xid,页面上最早未删除 XMAX,如果没有则为 0。
ItemIdData – 在 page header 之后,一个记录(偏移量,长度)对的数组,指向理论 tuple 项,每个 4 字节。
Free space – 未调配的空间(闲暇空间)。新项指针从这个区域的结尾开始调配,新项从其结尾开始调配。
Items – 用来寄存行数据 Tuple。
Special space– 索引拜访模式相干的数据。不同的索引拜访形式寄存不同的数据。在一般表中为空。
Tuple
页中的元组可细分为“一般数据元组和 TOAST 元组”。
TOAST (The Oversized-Attribute Storage Technique,超大属性存储技术) 次要用于存储变长数据,当待插入元组的数据大小大于约为 2KB (即页的 1/4) 时候,会主动启动 TOAST 技术来存储该元组。TOAST 较一般元组稍加简单些,这里次要针对一般元组文件进行阐明。
元组外部能够分为三局部,别离是:堆元组头部(23 字节)、NULL 值位图和用户存储的数据。
Pageinspect 扩大
PostgreSQL 在源码目录 contrib 下提供了许多扩大的性能,pageinspect 扩大模块提供的函数让你从低层次察看数据库页面的内容,这对于调试目标很有用。
装置
cd $PGSRC/contrib/pageinspect
make
sudo make install
简略应用
psql -d test
test=#create extension pageinspect; — 首次应用需创立 Extension
— 创立测试表
drop table if exists t1;
create table t1 (id int, name varchar(20));
insert into t1 values(1,’aaa’);
— 查看 page header&item
SELECT * FROM page_header(get_raw_page(‘t1’, 0));
select * from heap_page_items(get_raw_page(‘t1’,0));
— 更新一行数据
update t1 set name=’bbb’ where id=1;
查看 heap_page_items(),发现多了一条 item,起因是 PG 更新数据时并不批改原 tuple,而是插入一条新的 tuple,并标记原 tuple.t_xmax= 新 tuple 的事务 id。