5.3 元组的增删改

本节会介绍元组的增删改过程,并简要描述用于插入与更新元组的自由空间映射(Free Space Map, FSM)。

这里主要关注元组,页首部与行指针不会在这里画出来,元组的具体表示如图5.3所示。

图5.3 元组的表示图5.3 元组的表示

5.3.1 插入

在插入操作中,新元组将直接插入到目标表的页面中,如图5.4所示。

图5.4 插入元组图5.4 插入元组

假设元组是由txid=99的事务插入页面中的,在这种情况下,被插入元组的首部字段会依以下步骤设置。

  • Tuple_1
    • t_xmin设置为99,因为此元组由txid=99的事务所插入。
    • t_xmax设置为0,因为此元组尚未被删除或更新。
    • t_cid设置为0,因为此元组是由txid=99的事务所执行的第一条命令所插入的。
    • t_ctid设置为(0,1),指向自身,因为这是该元组的最新版本。

pageinspect

PostgreSQL自带了一个第三方贡献的扩展模块pageinspect,可用于检查数据库页面的具体内容。

testdb=# CREATE EXTENSION pageinspect;
CREATE EXTENSION
testdb=# CREATE TABLE tbl (data text);
CREATE TABLE
testdb=# INSERT INTO tbl VALUES(A);
INSERT 0 1
testdb=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page(tbl, 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |     99 |      0 |     0 | (0,1)
(1 row)

5.3.2 删除

在删除操作中,目标元组只是在逻辑上被标记为删除。目标元组的t_xmax字段将被设置为执行DELETE命令事务的txid。如图5.5所示。

图5.5 删除元组图5.5 删除元组

假设Tuple_1txid=111的事务删除。在这种情况下,Tuple_1的首部字段会依以下步骤设置。

  • Tuple_1
    • t_xmax被设为111。

如果txid=111的事务已经提交,那么Tuple_1就不是必需的了。通常不需要的元组在PostgreSQL中被称为死元组(dead tuple)

死元组最终将从页面中被移除。清除死元组的过程被称为清理(VACUUM)过程第六章 清理过程(VACUUM) 将介绍清理过程。

5.3.3 更新

在更新操作中,PostgreSQL在逻辑上实际执行的是删除最新的元组,并插入一条新的元组(图5.6)。

图5.6 两次更新同一行图5.6 两次更新同一行

假设由txid=99的事务插入的行,被txid=100的事务更新两次。

当执行第一条UPDATE命令时,Tuple_1t_xmax被设为txid 100,在逻辑上被删除;然后Tuple_2被插入;接下来重写Tuple_1t_ctid以指向Tuple_2Tuple_1Tuple_2的头部字段设置如下。

  • Tuple_1
    • t_xmax被设置为100。
    • t_ctid(0,1)被改写为(0,2)
  • Tuple_2
    • t_xmin被设置为100。
    • t_xmax被设置为0。
    • t_cid被设置为0。
    • t_ctid被设置为(0,2)

当执行第二条UPDATE命令时,和第一条UPDATE命令类似,Tuple_2被逻辑删除,Tuple_3被插入。Tuple_2Tuple_3的首部字段设置如下。

  • Tuple_2
    • t_xmax被设置为100。
    • t_ctid(0,2)被改写为(0,3)
  • Tuple_3
    • t_xmin被设置为100。
    • t_xmax被设置为0。
    • t_cid被设置为1。
    • t_ctid被设置为(0,3)

与删除操作类似,如果txid=100的事务已经提交,那么Tuple_1Tuple_2就成为了死元组,而如果txid=100的事务中止,Tuple_2Tuple_3就成了死元组。

5.3.4 空闲空间映射

插入堆或索引元组时,PostgreSQL使用表与索引相应的FSM 来选择可供插入的页面。

1.2.3节 所述,表和索引都有各自的FSM。每个FSM存储着相应表或索引文件中每个页面可用空间容量的信息。

所有FSM都以后缀fsm存储,在需要时它们会被加载到共享内存中。

pg_freespacemap

扩展pg_freespacemap能提供特定表或索引上的空闲空间信息。以下查询列出了特定表中每个页面的空闲率。

testdb=# CREATE EXTENSION pg_freespacemap;
CREATE EXTENSION

testdb=# SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
                FROM pg_freespace(accounts);
 blkno | avail | freespace ratio 
-------+-------+-----------------
     0 |  7904 |           96.00
     1 |  7520 |           91.00
     2 |  7136 |           87.00
     3 |  7136 |           87.00
     4 |  7136 |           87.00
     5 |  7136 |           87.00
....