本节会介绍元组的增删改过程,并简要描述用于插入与更新元组的自由空间映射(Free Space Map, FSM)。
这里主要关注元组,页首部与行指针不会在这里画出来,元组的具体表示如图5.3所示。
图5.3 元组的表示
5.3.1 插入
在插入操作中,新元组将直接插入到目标表的页面中,如图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 删除元组
假设Tuple_1
被txid=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 两次更新同一行
假设由txid=99
的事务插入的行,被txid=100
的事务更新两次。
当执行第一条UPDATE
命令时,Tuple_1
的t_xmax
被设为txid 100
,在逻辑上被删除;然后Tuple_2
被插入;接下来重写Tuple_1
的t_ctid
以指向Tuple_2
。Tuple_1
和Tuple_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_2
和Tuple_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_1
和Tuple_2
就成为了死元组,而如果txid=100
的事务中止,Tuple_2
和Tuple_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 ....
下一节:PostgreSQL在提交日志(Commit Log, clog)中保存事务的状态。提交日志(通常称为clog)分配于共享内存中,并用于事务处理过程的全过程。
本节将介绍PostgreSQL中事务的状态,clog的工作方式与维护过程。