pg 堆内元组 HOT(Heap Only Tuple)

8.3版本中实现了HOT特性,在更新行时,可以将新行于旧行放在同一个数据页,从而高效地利用索引与表的数据页,减少了不必要的清理过程。下面通过对比没有HOT和有HOT时,更新一行是怎样的过程。

测试表结构如下:

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

 

一、 没有HOT时的更新

表tb1有1000条元组,最后一个元组的id是1000,存储在第5个数据页中。最后一条元组被相应的索引元组引用,索引元组的key是1000,且tid是(5,1),如下图(a)。

pg 堆内元组 HOT(Heap Only Tuple)_第1张图片

在没有HOT时,更新一行是怎样的过程?

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

在该场景中,pg不仅要插入一条新的表元组,还要在索引页中插入新的索引元组(如上图b)。索引元组的插入消耗了索引页的空间,而且索引元组的插入和清理都是开销很大的操作。HOT的目的,就是降低这种影响。

 

二、 HOT如何工作

当使用HOT特性更新行时,如果被更新的元组存储在老元组的页面中,pg就不会再插入相应的索引元组,而是分别设置新老元组的t_informask2字段。

  • 新元组t_informask2字段设置为HEAP_ONLY_TUPLE
  • 老元组t_informask2字段设置为HEAP_HOT_UPDATED

pg 堆内元组 HOT(Heap Only Tuple)_第2张图片

pg 堆内元组 HOT(Heap Only Tuple)_第3张图片

 

1. pg如何通过索引扫描访问被HOT更新元组?

pg 堆内元组 HOT(Heap Only Tuple)_第4张图片

  • (1)找到指向目标数据元组的索引元组
  • (2)按索引元组指向的位置访问行指针数组,找到行指针1
  • (3)读取Tuple_1
  • (4)经由Tuple_1的t_ctid字段,读取Tuple_2

这种情况下,pg会读取两条元组,分别是Tuple_1和Tuple_2,并通过事务可见性判断哪条元组可见。但是,如果数据页中的死元组已被清理,就会有问题。例如上图中,如果Tuple_1由于是死元组而被清理,就无法访问Tuple_2了。

 

2. 索引修剪

为解决上面的问题,pg会在合适的时候进行索引修剪,即进行元组指针重定向,将指向老元组的指针重新指向新元组。

pg 堆内元组 HOT(Heap Only Tuple)_第5张图片

  • (1)找到指向目标数据元组的索引元组
  • (2)按索引元组指向的位置访问行指针数组,找到行指针1
  • (3)通过重定向的行指针1,redirect到行指针2
  • (4)通过行指针2,读取Tuple_2

可能的话,索引修剪在任何时候都可能发生,确切时机过于复杂,可参考README.HOT文件

 

3. 碎片整理

在pg执行剪枝时,如果可能,会挑选合适时机来清理死元组,这种操作称为碎片整理。

pg 堆内元组 HOT(Heap Only Tuple)_第6张图片

由于碎片整理不涉及索引元组的移除,所以比起常规vacumm开销要少得多。

 

三、 HOT不可用的场景

 

1. 更新的元组与老元组不在同一页面

此时执行新元组的索引元组也会被添加至索引页,如图a。

pg 堆内元组 HOT(Heap Only Tuple)_第7张图片

 

2. 索引键更新

当索引键更新时,会在索引页中插入一条新的索引元组,如图b。

pg 堆内元组 HOT(Heap Only Tuple)_第8张图片

pg_stat_all_tables视图提供了每个表的统计信息视图,也可以参考。

你可能感兴趣的:(pg 堆内元组 HOT(Heap Only Tuple))