Postgresql之HOT技术简析

背景:
PG实现了MVCC,但是没有引入undo表空间,为实现多版本读,所有的update操作都插入一个新版本行(delete-marked-old + insert-new),数据行的各个版本从老到新形成一个单向链表,即update chain。
pre-8.3,索引块也是采用同样的策略,即便update没有更新任何索引列,举例说明:
表t有两个列(id , name),其中name上有索引 ind_name,现在表有2个数据行,分别称为tuple1和tuple2
(1, 'a')
(0, 'b')
执行update t set id =id+ 1 where name = 'a'
现在表上有3个数据行
(1, 'a') ---> (2, 'a')  这个是update chain, (1, 'a')此时被标记为deleted,且其指向(3, 'a')
(0, 'b')
而索引ind_name同样会新增一个entry,尽管没有更新索引列name
('a', tuple1_pos)  --> ('a', tuple1_pos),第2个entry没有存在的必要
('b', tuple2_pos)
这会导致索引占用更多的空间,提升访问代价

改进
从8.3开始,当update语句同时满足下述两个条件时,索引不会新增entry:
1 update没有更新索引列;
2 表块有足够空间容纳update产生的新版本行,即同一update chain上的所有版本必须位于同一个数据块中。
同样的例子,执行update t set id =id+ 1 where name = 'a'后
现在表上有3个数据行
(1, 'a') ---> (2, 'a')
(0, 'b')
而索引ind_name没有改变
('a', tuple1_pos)
('b', tuple2_pos)

尽管数据行从(1, 'a')变成了(2, 'a'),但是ind_name的索引项仍然指向(1, 'a'),回表时通过遍历update chain找到(2, 'a')

这种改进被称为HOT(heap only tuple)

实现原理
PG数据块由几部分组成: 块头;行指针;数据行;块尾
其中行指针指向对应行物理位置,其组成为 (lp_offset, lp_flags, lp_len) = (15b, 2b, 15b)
lp_offset为元组在块内偏移量,2^15=32768,因此PG的数据块最大为32K;
lp_flags描述元组的状态,有4个候选值:未使用;正常使用;hot重定向;死亡
lp_len为元组的长度
每个数据行都有一个行指针,当全表扫描时,会读取块内的所有行指针以获取数据行。

数据行的头部包含一些元数据字段,其中ctid标注该行物理位置(offset, len),和行指针有重复嫌疑,当形成update chain时,ctid指向的是链表中下一个版本的位置,依靠它才能完成遍历。

同时,PG不允许有行链接出现,因为这样会导致索引回表访问时产生额外IO,所以当数据块不足以容纳一个update chain上的所有元素时,便会在其他数据块新建一条update chain,相应的索引也会新增一个entry。
当不断执行update t set id =id +1 where name ='a' 时,block1会被撑爆,最终会变成如下形式:
block 1
(1, 'a') ---> (2, 'a') ---> .......
(0, 'b')

block 2
(N, 'a') ---> (N + 1, 'a') 
此时索引会新增一个entry
('a', tuple1_pos)   -->   ('a', tuple3_pos)
('b', tuple2_pos)


延伸
innodb的索引元组结构为(索引列,主键),每次回表都通过主键扫描B+树来定位数据块,再借助页目录在块内定位数据行物理位置。
这种方式的优点是数据行的物理位置发生改变且索引列值不变时,不需要更新索引,缺点是每次回表会多消耗2个IO(普通B+树一般为3层)。
因此,当update没有更新索引列时,相应的索引完全不受影响,不存在类似PG的烦恼。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15480802/viewspace-2121757/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/15480802/viewspace-2121757/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值