1.HOT概述在PostgreSQL中,由于其多版本的特性,当我们更新数据时,并不是直接修改元数据,而是通过插入一行新数据来间接修改。更新。当表上有索引时,由于新插入的数据,索引也必须同步更新。这在索引较多的情况下,必然会对更新的性能产生很大的影响。为了解决这个问题,pg从8.3版本开始引入了HOT(HeapOnlyTuple)机制。原理大致是当update不是索引字段时,我们将旧元组指向新元组,原来的索引不变,仍然指向旧元组,但是我们可以将旧元组作为间接访问新的元组,这样就不需要更新索引。2.HOT实现技术细节使用HOT更新需要满足两个前提条件:新元组和旧元组必须在同一页;无法更新索引字段。当我们执行热更新时,我们首先将旧元组的t_informask2标志设置为HEAP_HOT_UPDATED,将新元组分别设置为HEAP_ONLY_TUPLE。更新如下图所示:我们将tuple1更新为tuple2,分别设置这两个tuple的t_informask2标志,然后tuple1的ctid指向tuple2,tuple2指向自身。但是这种方式有一个明显的问题。我们都知道pg会定期vacuum清理那些死元组。如果我们这里通过tuple1访问tuple2,那么如果tuple1的死元组被清理掉了怎么办?因此,pg会适时重定向行指针,将旧元组的行指针指向新元组的行指针。这个过程称为修剪。所以剪枝之后,我们通过HOT机制访问数据是这样的:1.通过索引元组找到旧元组的行指针1;2、通过重定向后的行指针1找到行指针2;3、找到行指针2通过行指针2找到新的元组tuple2。这样,即使把旧的元组tuple1清理掉,也没有任何作用。HOT对应的wal日志实现:对于HOT的更新操作,wal日志中记录的信息主要由xl_heap_update结构存储。如果新元组存储在block_id为0的块上,如果不是XLOG_HEAP_HOT_UPDATE,则旧元组将存储在block_id为1的块上。相反,如果block_id为1的块没有被使用,则认为是XLOG_HEAP_HOT_UPDATE。3.什么时候剪枝我们前面提到,旧行的行指针会被重定向到新行的行指针。这个过程称为修剪。那么什么时候修剪呢?一般来说,当我们执行select、update、insert、delete等命令时,都有可能触发剪枝。触发机制大致有两种情况:上次更新未能在此页面找到足够的空间;当前页面的剩余空间空间小于fill-factor的值,最多小于10%。另外,在剪枝的时候,也会选择一个合适的时机清理死元组。此操作称为碎片整理。当我们检索元组并发现可用空间少于10%时,就会进行碎片整理。与修剪不同,碎片整理不会在插入时发生,因为该操作不会检索行。与普通的vacuum操作相比,片段清理不涉及索引元组的清理,开销也比常规清理小很多,这是通过PageRepairFragmentation函数实现的。这就是为什么HOT要求新旧元组在同一页中的原因。虽然理论上我们可以将行指针链表指向不同的页,但是我们不能使用页局部操作来清理碎片。4、HOT的不足我们前面提到,HOT的前提之一是:更新的列不能是索引列。需要注意的是,当被更新的列是索引列时,不仅会修改该列上的索引,还会修改整个表上的所有索引。示例:创建测试表:bill=#createtablea(idint,c1int,c2int,c3int);CREATETABLEbill=#insertintoaselectgenerate_series(1,10),random()*100,random()*100,random()*100;INSERT010bill=#在(id)上创建索引idx_a_1;CREATEINDEXbill=#在(c1)上创建索引idx_a_2;CREATEINDEXbill=#在(c2)上创建索引idx_a_3;CREATEINDEX
