1.4 读写元组的方式
最后本章将描述写入与读取堆元组的方式。
1.4.1 写入堆元组
假设有一个表仅由一个页面组成,且该页面只包含一个堆元组。此页面的pd_lower指向第一个行指针,而该行指针和pd_upper都指向第一个堆元组,如图1.5(1)所示。
当写入第二个元组时,它会被放在第一个元组之后。第二个行指针写入到第一个行指针的后面,并指向第二个元组。pd_lower 更改为指向第二个行指针,pd_upper 更改为指向第二个堆元组,如图1.5(2)所示。页面内的首部数据(例如pd_lsn、pg_checksum和pg_flag)也会被改写为适当的值,具体细节将在第5.3节和第9章中描述。
图1.5 堆元组的写入
1.4.2 读取堆元组
这里简述两种典型的访问方式:顺序扫描与B树索引扫描。
· 顺序扫描——通过扫描每一页中的行指针,依序读取所有页面中的所有元组,如图1.6 (1)所示。
· B树索引扫描 —— 索引文件包含索引元组,索引元组由一个键值对组成,键为被索引的列值,值为目标堆元组的 TID。进行索引查询时,首先使用键进行查找,如果找到了对应的索引元组,PostgreSQL就会根据相应值中的TID来读取对应的堆元组。使用B树索引找到索引元组的方法请参考相关资料,这一部分属于数据库系统的通用知识,限于篇幅不再详细展开。例如在图1.6(2)中,对于所获索引元组中TID的值,区块号 = 7,偏移号 = 2,这意味着目标堆元组是表中第7页的第2个元组,因而PostgreSQL可以直接读取所需的堆元组,以避免对页面进行不必要的扫描。
图1.6 顺序扫描和B树索引扫描
PostgreSQL还支持TID扫描、位图扫描和仅索引扫描。
TID扫描是一种通过使用所需元组的TID直接访问元组的方法。例如要在表中找到第0个页面中的第1个元组,可以执行以下查询:
sampledb=# SELECT ctid, data FROM sampletbl WHERE ctid = '(0,1)'; ctid | data -------+----------- (0,1) | AAAAAAAAA (1 row) sampledb=# EXPLAIN SELECT ctid, data FROM sampletbl WHERE ctid = '(0,1)'; QUERY PLAN ---------------------------------------------------------- Tid Scan on sampletbl (cost=0.00..1.11 rows=1 width=38) TID Cond: (ctid = '(0,1)'::tid)
仅索引扫描将在第7章中详细介绍。