数据库元组插入更新时,索引也需要进行相应维护,因为pg的老元组不会实时清理,那么在更新后索引中就会多出一条索引记录指向新元组,这样造成索引膨胀,维护代价变大。pg为了避免这个问题,从8.3版本开始采用HOT(heap only tuple)解决这个问题,下面简单介绍一下技术原理。
普通更新
如下图所示,当tuple发生更新后,需要新增一条tuple记录到页面中,此时索引中也对应新增了一条记录,该记录指向tuple2的line pointer。这样的话每次更新都需要在索引页面插入新纪录,维护开销太大,而且会造成索引膨胀。Pg采用hot技术解决这个问题。
HOT更新
在使用hot更新时,元组更新后不会在索引页面新建相应记录,而通过在新老元组上设置标志位使得老元组指向新元组,形成新旧元组“链”解决这个问题。
在元组结构的t_informask2字段中有两个标记位,heap_hot_update和heap_only_tuple,在更新tuple1时,postgresql会将tuple1(老元组)的标记位置为heap_hot_update,代表该元组是经过hot更新的行,同时将tuple2(新元组)的标记位置为heap_only_tuple。具体过程如下:
1.首先找到目标数据的索引元组;
2.然后通过索引元组中的位置,访问行指针数组,找到行指针1;
3.读取tuple1;
4.发现tuple1的标记位是heap_hot_update,表明该元组是经过hot更新的元组,并不是真实要读取的数据,于是通过tuple1的t_ctid字段读取tuple2。
动态修剪
上面访问数据的过程其实访问了tuple1和tuple2两个数据块,这时我们可能会考虑到一个问题,因为tuple1是旧元组,它会在合适时机被vacuum掉,这时就无法通过tuple1的ctid字段定位到tuple2了,为了解决这个问题,postgresql会在合适的时候进行line pointer的重定向(redirect),将tuple1的line pointer重定向到line pointer2,这个过程称为动态修剪。
此时访问新元组的流程如下:
1.首先找到目标数据的索引元组
2.然后通过索引元组中的位置,访问行指针数组,找到行指针1
3.通过行指针的重定向,找到行指针2
4.通过行指针2直接定位tuple2
HOT技术的适用场景
当然HOT技术也不是万能的,它也有不适用的场景,比如下面两个场景:
1.当更新的元组和老元组不在同一个page中时,新旧元组链是不能跨越页面的,指向该元组的索引元组也会被添加到索引页面中。
2.当索引的key值更新时,原有索引记录中的key无法再定位到正确元组,此时会在索引页面中插入一条新的索引元组。