PG_Heap Only Tuple(HOT)剖析

Postgresql在8.3版中引入了HOT特性,以便在Update操作中,当更新的新行和旧行存储在相同的表数据页中时,能有效地使用索引和表的页;同时也减少了Vacuum处理的必要性。

无HOT之前PG-update存在的问题

假设表‘tbl’存在两个列 ‘id’ 和‘data’; ‘id’ 是 ‘tbl’的主键

testdb=# \d tbl
                Table "public.tbl"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 id     | integer |           | not null | 
 data   | text    |           |          | 
Indexes:
    "tbl_pkey" PRIMARY KEY, btree (id)

表' tbl '有1000个元组;id为' 1000 '的最后一个元组存储在表的第5页。最后一个元组是从对应的索引元组中指向的,其索引键是' 1000 ',tid是'(5,1)',如图1.1(a)

Fig. 1.1. Update a row without HOT

我们考虑用以下SQL进行更新:

testdb=# UPDATE tbl SET data = 'B' WHERE id = 1000;

在这种情况下,PostgreSQL不仅会在表中插入新的元组,还会在索引页中插入新的索引元组。见图1.1(b)

 索引元组的插入会消耗索引页空间,而且索引元组的插入和删除成本都很高。HOT降低了这些问题的影响。

HOT内部运行原理剖析

当一行数据用HOT特性更新时,如果更新的行和旧行存储在相同的表页面中,PostgreSQL将不插入相应的新索引元组,而是分别在在旧元组的t_informask2标志位中设置HEAP_HOT_UPDATED,新元组的t_informask2标志位中设置HEAP_ONLY_TUPLE ;见图1.2和1.3

此外,在执行剪枝和碎片整理操作中,HEAP_HOT_UPDATED和HEAP_ONLY_TUPLE位都将被使用。

Fig. 1.2. Update a row with HOT

 Fig. 1.3. HEAP_HOT_UPDATED and HEAP_ONLY_TUPLE bits

以下,将剖析PostgreSQL如何在用HOT更新元组之后使用索引扫描访问更新后的元组。见图1.4(a)。

但是,如果删除表页中的死亡元组,就会出现一个问题。例如,在图1.4(a)中,如果' Tuple_1 '被删除,因为它是一个死元组,' Tuple_2 '将不能从索引中访问。

Fig. 1.4. Pruning of the line pointers

 操作步骤如下:

(1)查找指向目标元组的索引元组
(2)访问从索引元组中指向的行指针"[1]"
(3)读"Tuple_1"
(4)通过Tuple_1的t_ctid读取Tuple_2

但是,如果删除表页中的dead tuple,就会出现一个问题。例如,在图1.4(a)中,如果' Tuple_1 '被删除,因为它是一个dead tuple,' Tuple_2 '将不能从索引中访问。

为了解决这个问题,在适当的时候,PostgreSQL将指向旧元组的行指针重定向到指向新元组的行指针。在PostgreSQL中,这种处理称为剪枝。图1.4(b)描述了PostgreSQL如何访问修剪后更新的元组。

操作步骤如下:

(1)查找索引元组。
(2)访问从索引元组中指向的行指针"[1]"。
(3)通过重定向的行指针访问指向Tuple_2的行指针"[2]"。
(4)读取由行指针"[2]"指向的Tuple_2

如果可能的话,在执行SELECT、UPDATE、INSERT和DELETE等SQL命令时,将执行修剪处理。由于执行时间非常复杂,本章不详细描述。详情可以查看README.HOT

如果可能的话,PostgreSQL会在适当的时候删除dead tuple,就像在修剪过程中一样。在PostgreSQL的文档中,这种处理称为碎片整理。图1.5描绘了热的碎片整理。

Fig. 1.5. Defragmentation of the dead tuples

请注意,碎片整理的成本低于正常的Vacuum处理的成本,因为碎片整理不涉及删除索引元组。

使用HOT可以减少索引和页面表的消耗;这也减少了Vacuum处理必须处理的元组数量。因此,HOT对性能有很好的影响,因为它在update和Vacuum的处理过程中,减少了索引元组的插入次数。

 

不适用场景:

1、新旧元组不在同一个数据块中。

2、更新的列包含索引键。

 

 

参考文献:

《The Internals of PostgreSQL for database administrators and system developers》

《Postgresql官方文档》

 

posted @ 2021-02-05 16:46  DUAN的博客  阅读(544)  评论(0编辑  收藏  举报