本次阅读时长:

81349dced179d4eb81322e40d3be3685.gif

.

45186256f8e7100a05cc30439bca6b5d.gif

.

09cf95e914b39c8f73cf1d6bf1197192.gif

0fbb5e4bdc22665e81429c845377212d.png

作者:劳伦兹·阿尔伯

Laurenz Albe是CYBERTEC的高级顾问和支持工程师。

自2006年以来,他一直在PostgreSQL上工作并为PostgreSQL做贡献。

90b984a493edfa75a5f5ad76b123b280.png

HOT更新不是一项新功能。它们由2007年的commit 282d2a03dd引入,并首次出现在PostgreSQL 8.3中。

但是,由于HOT不在PostgreSQL文档中(尽管源树中有README.HOT),因此它并未得到应有的广泛了解。因此,本文将对这一概念进行说明,展示HOT的实际作用并给出调整建议。

什么是HOT?

HOT是“Heap Only Tuple”(仅元组堆)的缩写(与Overflow Update CHaining相比,它的缩写更好)。此功能克服了PostgreSQL处理UPDATE 效率低下的问题。

PostgreSQL实现UPDATE的问题

PostgreSQL通过将表行的旧版本保留在表中来实现多版本化– UPDATE会添加该行的新行版本(“tuple”)并将旧版本标记为无效。

在很多方面,PostgreSQL中的更新与删除后插入没什么区别。

ad115865d37d793145a0526a6c3d43d9.png

这有很多优点:

  • 不需要额外的存储区域来保存旧的行版本

  • ROLLBACK 回滚不需要撤销任何东西,而且非常快

  • 修改多行的事务没有溢出问题

但这也有一些缺点:

  • 最终必须从表中删除旧的,过时的(“死”)元组(VACUUM)

  • 大量更新的表可能会因死元组而变得“膨胀”

  • 即使没有修改索引属性,每次更新都需要添加新的索引条目,并且修改索引比修改表开销上大得多(必须维护顺序)

从本质上说,更新量大的工作负载对PostgreSQL来说是一个挑战。这就是HOT更新的帮助所在。

一个UPDATE例子

让我们创建一个包含235行的简单表:

CREATE TABLE mytable (   id  integer PRIMARY KEY,   val integer NOT NULL) WITH (autovacuum_enabled = off); INSERT INTO mytableSELECT *, 0FROM generate_series(1, 235) AS n;

该表的长度略大于一个8KB的块。让我们看看每个表行的物理地址(“当前元组ID”或ctid):

SELECT ctid, id, valFROM mytable;  ctid   | id  | val ---------+-----+----- (0,1)   |   1 |   0 (0,2)   |   2 |   0 (0,3)   |   3 |   0 (0,4)   |   4 |   0 (0,5)   |   5 |   0... (0,224) | 224 |   0 (0,225) | 225 |   0 (0,226) | 226 |   0 (1,1)   | 227 |   0 (1,2)   | 228 |   0 (1,3)   | 229 |   0 (1,4)   | 230 |   0 (1,5)   | 231 |   0 (1,6)   | 232 |   0 (1,7)   | 233 |   0 (1,8)   | 234 |   0 (1,9)   | 235 |   0(235 rows)

ctid由两部分组成:从0开始的“块号”和从1开始的“行指针”(块中的元组号)。

因此,前226行填充块0,最后9行在块1中。

让我们运行一个UPDATE:

UPDATE mytableSET val = -1WHERE id = 42; SELECT ctid, id, valFROM mytableWHERE id = 42;   ctid  | id | val --------+----+----- (1,10) | 42 |  -1(1 row)

新的行版本已添加到块1,该块仍具有可用空间。

HOT更新如何工作

为了了解HOT,让我们概括一下PostgreSQL中表页面的布局:

“行指针”数组存储在页面的开头,每个指针都指向一个实际的元组。这种间接引用允许PostgreSQL在内部重新组织页面,而不改变页面的外观。

仅元组堆(Heap Only Tuple)是未从表块外部引用的元组。而是在旧行版本中存储“转发地址”(其行指针号):

0c4a939f02d7d9178df051c5f51edf34.png

仅当该行的新版本和旧版本位于同一块中时,此方法才有效。该行的外部地址(原始行指针)保持不变。要访问仅元组堆,PostgreSQL必须遵循块中的“ HOT链”。

HOT更新的优点

HOT更新有两个主要优点:

  1. PostgreSQL不必修改索引。由于元组的外部地址保持不变,因此仍可以使用原始索引条目。索引扫描遵循HOT链以找到适当的元组。

  2. 无需删除死元组即可执行VACUUM。如果一行上有多个HOT更新,则HOT链会很长。现在,任何后端进程处理的块,当检测到带有无效元组的HOT链,都将尝试锁定并重新组织该块,从而删除中间元组。这是可能的,因为没有外部引用这些元组,这大大减少了UPDATE繁重工作负载时对VACUUM的需求。

HOT更新要求

使用HOT更新有两个条件:

  1. 包含更新行的块中必须有足够的空间

  2. 在已修改值的任何列上均未定义索引

第二个条件并不明显,并且是该功能的当前实现所必需的。

在表上使用fillfactor以获取HOT更新

您可以确保满足上面的第二个条件,但是如何确保表块中有足够的可用空间呢?

为此,我们有存储参数fillfactor。它是介于10到100之间的值,它确定INSERT将填充表块的百分比。如果选择的值小于默认值100,则可以确保每个表块中都有足够的空间用于HOT更新。

fillfactor的最佳值取决于平均行的大小(较大的行需要较低的值)和工作负载。

请注意,fillfactor在现有表格上进行设置不会重新排列数据,它仅适用于将来的 INSERT。但是您可以使用VACUUM (FULL)或CLUSTER重写表,这将遵从fillfactor的新设置。

有一种简单的方法可以查看您的设置是否有效以及是否获得HOT更新:

SELECT n_tup_upd, n_tup_hot_updFROM pg_stat_user_tablesWHERE schemaname = 'myschema'  AND relname = 'mytable';

这将显示自上次调用函数pg_stat_reset()以来的累计计数。

检查n_tup_hot_upd(HOT更新计数)的增长速度是否与n_tup_upd(更新计数)一样快,以查看是否获得了所需的HOT更新。

HOT更新的示例

让我们更改表的填充因子fillfactor,然后重复上面的实验:

TRUNCATE mytable; ALTER TABLE mytable SET (fillfactor = 70); INSERT INTO mytableSELECT *, 0 FROM generate_series(1, 235); SELECT ctid, id, valFROM mytable;   ctid   | id  | val ---------+-----+----- (0,1)   |   1 |   0 (0,2)   |   2 |   0 (0,3)   |   3 |   0 (0,4)   |   4 |   0 (0,5)   |   5 |   0... (0,156) | 156 |   0 (0,157) | 157 |   0 (0,158) | 158 |   0 (1,1)   | 159 |   0 (1,2)   | 160 |   0 (1,3)   | 161 |   0... (1,75)  | 233 |   0 (1,76)  | 234 |   0 (1,77)  | 235 |   0(235 rows)

这次,块0中的元组更少,并且仍有一些空间可用。

让我们UPDATE再次运行,看看这次效果如何:

UPDATE mytableSET val = -1WHERE id = 42; SELECT ctid, id, valFROM mytableWHERE id = 42;   ctid   | id | val ---------+----+----- (0,159) | 42 |  -1(1 row)

更新的行被写入到块0中,并且是HOT更新。

结论

HOT更新是使PostgreSQL能够处理大量UPDATE工作负载的一个特性。在UPDATE繁重的工作负载中,避免索引更新的列并将a设置fillfactor为小于100可以节省很多时间,某些场景下这可能是一个救命之策。

如果您对HOT更新的性能测量感兴趣,请参阅我的同事Kaarel Moppel撰写的这篇文章。

I Love PG

关于我们

中国开源软件推进联盟PostgreSQL分会(简称:中国PG分会)于2017年成立,由国内多家PostgreSQL生态企业所共同发起,业务上接受工信部中国电子信息产业发展研究院指导。中国PG分会是一个非盈利行业协会组织。我们致力于在中国构建PostgreSQL产业生态,推动PostgreSQL产学研用发展。

欢迎投稿

做你的舞台,show出自己的才华 。

投稿邮箱:partner@postgresqlchina.com

                    ——愿能安放你不羁的灵魂

往期回顾

0e6b07449ad8decd2e98e84009fbbc43.png 

活动成就

PostgresConf.CN & PGConf.Asia2020大会预告

PG ACE计划的正式发布

三期PostgreSQL国际线上沙龙活动的举办

六期PostgreSQL国内线上沙龙活动的举办

PGCM高级认证培训的正式开启

经典文章

PostgreSQL 13.0 正式版发布通告

深度报告:开源协议那些事儿

从“非主流”到“潮流”,开源早已值得拥有

Oracle中国正在进行新一轮裁员,传 N+6 补偿

PostgreSQL与MySQL版权比较

PostgreSQL与Oracle:成本、易用性和功能上的差异

使用ora2pg完成从Oracle到Postgres的迁移

精彩专辑

PostgreSQL活动篇

e160234e1fbeea0e826cb26cd3f697e6.gif

f9e65fd608e833616a7b9fae1b9957f8.png

↓↓↓

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐