PostgreSQL之堆表存储(Heap Table)

我们知道PostgreSQL中的表存储结构属于堆表(Heap Table),这是与MYSQL不同的(MYSQL中聚集索引表Clustering Index)。那么堆表和聚集索引表到底有什么不同,我们就一起来学习一下。

首先我们了解一下什么是堆表,以下关于堆表的描述来自SQL Server关于堆表的说明

什么是堆表?

SQL Server关于堆表的描述如下,

  • 没有聚集索引的表
  • 堆表在sys.partitions里有1条index_id = 0 的记录
  • 数据存储没有任何的顺序,插入数据也没顺序
  • 由于数据没有任何顺序,查询数据会非常慢
  • 数据页之间没有相互链接
  • 从数据页读取数据,需要从IAM(Index Allocation Map)页里找页号
  • 在sys.system_internals_allocation_units系统视图里,first_iam_page列,指向IAM页链中的第一个IAM页,它用来管理堆表的空间分配
  • 因为没有聚集索引,碎片不能通过重建索引(rebuilding the index)处理
  • SQL Server使用IAM页在堆结构里导航。分配给堆的页没有任何的顺序,且不相互链接。数据页之间唯一的逻辑关联是存在IAM页里的信息。

抛开SQL Server本身的一些用法以外,我们可以总结出来堆表具有的主要特点是:数据存储没有任何顺序,需要借助索引页来提升查询数据性能。下图也是一个示例说明需要通过索引页(IAM)来寻找对应的堆表数据页。
在这里插入图片描述

PostgreSQL中的堆表结构

OK,上述了解完什么是堆表之后,我们再来学习一下PG中的堆表。在PG数据库中,一个数据文件是由多个固定长度的页(或者称为块)组成,每个页的默认大小为8KB。在每个文件内部,这些页都有一个从0开始的页编号(block number)。当文件被填充时,PG通过增加一个新的空页到文件末尾来增加文件大小。

在这里插入图片描述
上图示例描述一个数据文件的内部结构,从图中可以看出,一个表文件内部由从0至N编号的页面构成,每个页为8KB。右边则是每个页面的内部构成。每个页面包括以下内容:

  • heap tuples -即heap表中的数据记录。这些记录是从页面的底端向上存放的。每个tuple内部的结构我们单独再描述。
  • line pointers -每个line pointer由4个字节表示,是一个指向具体某一条heap tuple的指针,也被称为item pointer。Line pointers组成一个简单的数组,负责索引到对应记录。每个line pointer在block内是从1开始进行编号,也被称为偏移号(offset number)。当一个新的记录被添加到页面时,就会增加一个新的line pointer与之对应。
  • header data -即页面的头信息,放在页面的开始部分。由24字节表示,其中包含一些页面描述的通用信息。主要的内容有:
    (1)pd_lsn -保存页面上次修改生成的XLOG的LSN信息。8字节的无符号整数,与WAL机制有关。
    (2)pd_checksum -保存页面的checksum值(9.3及之后版本)。
    (3)pd_lower,pg_upper -pd_lower指向line pointers的末尾位置,pd_upper指向最新的heap tuple位置。
    (4)pd_special -用于索引。如果此页面是表页面,指向页面的末尾。如果此页面是索引页面,指向特殊空间的开始(此空间根据索引的类型是BTree、GiST还是GIN保存特定的数据)。

在line pointers结尾到最新tuple开始之间的空间代表空闲空间或空洞。
为了确定表中某一个tuple的位置,PG内部使用tuple identifier(TID) 。TID由一组值构成:包括这个tuple页面的页编号block number,以及指向这个tuple的具体的偏移号offset number。即TID=block number + offset number

下面我们具体看一个如何通过全表扫描/索引扫描来定位到需要的表记录。

读取Heap Tuples

读取Heap Tuples通常有两种方式,按sequential scan和按B-Tree Index Scan,

  • Sequential Scan - 通过扫描每个页面中所有line pointers来达到顺序读取所有页面中的tuples记录。
  • B-Tree index Scan - Index文件包括Index tuples,每个Index tuple由一个index key和一个指向对应heap tuple的TID构成。根据key找到对应的Index tuple时,利用对应的TID来读取对应的heap tuple。比如在下图中,所获取的index tuple对应的TID为(block=7,offset=2),表示对应的heap tuple是表对应的第7个页面中的第2个tuple记录。

在这里插入图片描述

写入Heap Tuples

根据上述内容,我们知道写入heap tple是从一个页面的底部往上写,每写入一条heap tuple会同时写一个对应的指针即line pointer,同时也会更新其他相关的页面Head信息。具体的说,假如开始一个页面中只包含一条记录Tuple 1,当写入Tuple 2时,这个Tuple会被放置在Tuple 1前面。第2个line pointer被写入放置在第一个line pointer后面并指向Tuple 2。pd_lower会被更新指向第2个line pointer末尾,pd_upper会被更新指向Tuple 2的开始位置。其余的页面header数据如pd_lsn,pg_checksum,pg_flag等会相当的被写入对应的新值。
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据源的港湾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值