在只接收 INSERT 的表上运行 VACUUM 是否值得?

foo*_*100 19 postgresql performance vacuum postgresql-performance

在 2015 年的 re:Invent 演讲中,AWS 提到真空不仅应该在更新或删除之后运行,而且还应该在插入之后运行。这是谈话的相关部分:

http://www.youtube.com/watch?v=tZXp19q8RFo&t=16m2s

据说即使块只收到插入,也必须对块进行一些清理,并且可以在第一次选择块时(减慢读取速度)或在真空期间进行清理。这是真的吗?如果是这样,究竟必须进行哪些清理工作?

Jim*_*sby 15

tl;dr:提交后读取数据的第一个进程将设置提示位。这会弄脏页面,创建写入活动。另一件事VACUUM(但不是其他命令)会在适当的情况下将页面标记为全可见。VACUUM最终将不得不打表来冻结元组。

插入后需要完成的工作并不是真正的清理,至少不是其他工作VACUUM通常所做的。在我进入细节之前,请注意这个答案是基于当前(未发布的)9.6 代码,我忽略了流复制的影响,即使它会影响可见性。

由于MVCC,每次 Postgres 评估元组是否应该对查询可见时,它必须考虑创建元组的事务(记录在 xmin 隐藏字段中)是否已提交,以及其他一些标准。该检查是昂贵的,因此一旦知道事务对所有当前打开的事务都是可见的,就会在元组标头上设置一个“提示位”来指示这一点。该位的设置会弄脏页面,这意味着必须将其写入磁盘。如果下一个读取数据的命令SELECT突然产生大量写入流量,这可能会非常令人困惑。VACUUM在插入提交后运行 a将避免这种情况。另一个重要的区别是VACUUM将始终提示页面上的元组(只要它在页面上获得清理锁),但大多数其他命令只会提示插入事务是否在命令开始之前提交。

编写所有这些提示位的一个重要点是VACUUM可以限制的(默认情况下自动清理是限制的)。其他命令不受限制,会尽快生成脏数据。

VACUUM是将页面标记为全可见的唯一方法,这是某些操作(特别是仅索引扫描)的重要性能考虑因素。如果你做一个大的插入,很可能有很多页面只有新插入的元组。VACUUM可以潜在地将这些页面标记为全部可见,VACUUM前提是启动时最旧的运行事务比插入数据的事务新

由于 MVCC 的工作方式,在超过 20 亿笔交易之前插入的元组必须标记为“冻结”。默认情况下,autovacuum 将每 200M 事务启动一次。在批量插入后运行将vacuum_freeze_min_age 设置为0 的手动vacuum 可以帮助减少这种影响。更积极的是,您可以VACUUM FREEZE在插入后在桌子上运行。这将在下一次冻结扫描发生时“重置时钟”。

如果你想知道具体的细节,请查看HEAPTUPLE_LIVE调用HeapTupleSatisfiesVacuum()inside后的案例lazy_scan_heap()。另见HeapTupleSatisfiesVacuum()自身,并将其与HeapTupleSatisfiesMVCC().

我的另外两个演讲可能很有趣。第一个视频可从http://www.pgcon.org/2015/schedule/events/829.en.html 获得,而第二个视频(我认为更好一点)在https://www.youtube 上。 com/watch?v=L8nErzxPJjQ

  • COMMIT 和 ROLLBACK 实际上并不涉及数据页,所以不,这些命令*特别*永远不会暗示。DML 命令仍然可以设置 xmin 和 xmax 提示状态,无论是为其他事务标记的元组,还是可能甚至是当前事务标记的元组。 (3认同)