PostgreSQL VACUUM当前状态

PostgreSQL VACUUM当前状态

原文: http://rhaas.blogspot.jp/2018/01/the-state-of-vacuum.html

在最近的一篇博客文章中,我谈到了为什么每一个MVCC的实现系统都需要某种方案来删除旧的行版本,以及VACUUM如何满足PostgreSQL的需要。在这篇文章中,我想回顾一下近年来VACUUM改进的历史、在PostgreSQL中存在的VACUUM状态,以及将来如何改进它。

当我第一次开始使用PostgreSQL时,自动VACUUM并不存在,我也不知道需要手动真空。几个月后,我想知道为什么我的数据库速度这么慢。我在cron中加入了VACUUM命令,按计划每6小时运行一次,当时满足了我的需要,但这只是因为我的数据库很小,并且处理的流量有限。在许多环境中,更新和删除操作将以某些表为目标的频率要比其他表高得多,因此一些表将比其他表更快地累积死行版本,因此,一个VACUUM与下一个VACUUM之间的间隔也应有所不同。如果这种环境的用户频繁地运行全数据库VACUUM,以满足其更新最频繁的表的需要,那么他们会将更新较轻的表VACUUM,这远远超出了必要的范围,这很浪费精力的。如果他们要减少全数据库真空的频率,以避免浪费精力,那么更新频繁的表就不会经常被清除,而且磁盘上的大小也会随着死行版本的增加而增加,也就是所谓的“臃肿”。

PostgreSQL 8.3是第一次发布了自动VACUUM。在默认情况下,首次启用了自动VACUUM。它还提供了一种多进程架构,这意味着多个表可以同时被自动VACUUM。当然,自动VACUUM还可以计算出哪些表应该被VACUUM,基本上(虽然不是完全),不需要手动VACUUM。这是向前迈出的一大步;第一次,不是每个PostgreSQL用户在配置PostgreSQL时都需要担心配置VACUUM。这真是一个好日子。

PostgreSQL 8.4提供了两个巨大的改进。在旧版本中,有一个固定大小的空闲空间映射,只能通过更改配置参数和重新启动服务器来调整大小。如果数据库中有空闲空间的页面数超过了自由空间映射的配置大小,服务器将无法跟踪某些包含空闲空间的页面,这通常会导致数据库膨胀。PostgreSQL 8.4引入了一个新的、动态大小的自由空间地图,它从未丢失自由空间的踪迹。它还增加了一个可见性地图,这使得VACUUM可以避免在每次新的VACUUM期间再次扫描物理表上不变的部分。但是,仍然需要每次扫描所有索引,并定期扫描整个表,以防止事务ID回绕。与普通VACUUM相比,用于防止事务ID封装的VACUUM通常很少;默认情况下,前者每二亿次写入事务就触发一次,而默认情况下,当表中死元组的数量超过表中估计的元组数的20%+20%时,触发后者。在每种情况下,都可以通过调整配置参数来更改这些值。

自PostgreSQL 8.4以来的几项改进都集中于防止VACUUM陷入困境。存在于多个变体中的基本问题是,如果一个自动VACUUM过程等待一些长期无法使用的锁,那么

  1. 它试图VACUUM的表不会被该过程或任何其他过程VACUUM,因为只有一个过程可以一次VACUUM一个表。
  2. 系统中任何其他表上都可以运行VACUUM,这可能意味着其他表也不会被迅速地VACUUM。 这两个问题都会导致表膨胀。PostgreSQL 9.1通过自动VACUUM,跳过因VACUUM而不能立即获得关系锁的表,从而对此做了一些改进;因为自动VACUUM技术每分钟都会重试一次,所以不会有任何伤害。PostgreSQL 9.2改进了一些功能,使系统可以跳过无法立即获得清理锁的单个表块,除非表块包含需要删除或冻结的元组,并且VACUUM是为了防止事务ID回绕。PostgreSQL 9.5减少了btree索引扫描在上次访问的索引页上保留一个槽的情况,这消除了大多数等待索引扫描的VACUUM情况。正如这一系列改进所表明的,索引扫描卡住的问题还没有完全解决,但是我们已经逐步减少了发生这种情况的情景。

还有一些改进旨在减少VACUUM扫描每个堆页的次数。在PostgreSQL 9.3之前,没有在一个VACUUM和下一个VACUUM之间修改的表页将被标记为“所有”--第二个VACUUM可见,并被未来的空白所跳过,除非被触发以防止事务ID回绕。在PostgreSQL 9.6中,即使在为防止事务ID回绕而触发的v中,也可以跳过页面。为了做到这一点,可见性图得到了进一步的改进,不仅可以跟踪页面是否是所有可见的--也就是说,已知不包含任何死行版本--而且它们是否都已冻结。已知不包含任何可能导致最终事务ID回绕的元组。后一类中的页面可以无条件跳过;它们不可能出于任何目的对VACUUM感兴趣。

除了上述内容外,多年来还不断改进了日志记录和其他一些效率改进。

还有什么要做呢?PostgreSQL开发社区在减少VACUUM执行不必要的表页扫描方面取得了很大进展,但在避免不必要的索引页扫描方面基本上没有任何进展。例如,即使没有找到死行版本的VACUUM仍然会扫描btree索引以回收空页。目前正在努力改善这一状况,但在btree索引如何与事务ID汇总交互的一些细节上,他们遇到了一些障碍。在只有少数(但不是很多)死行版本的情况下,改进行为也是可取的。如果1TB表包含10个死行版本,特别是如果它们都在同一页上,则扫描所有索引以删除这些行版本的索引指针可能没有意义。但是,在只有少数死行版本的情况下,自动VACUUM通常不会触发,这一事实缓解了这个问题。

VACUUM仍然是一种批量操作。在某些方面,这是好的,因为将大量的相关操作一起打包到一个单独的大容量操作中,往往比一个接一个地执行这些操作更有效率。然而,这也意味着当VACUUM接合时,特别是经过很长一段时间没有运行时,资源利用率可能会令人吃惊,而在大型表上,它可以持续很长一段时间。大桌子的VACUUM是否可以分成一系列较小的阶段,这似乎是值得考虑的问题。

对于大型数据库来说,基于成本的VACUUM延迟的默认设置太小了。默认情况下,自动VACUUM运行时的成本延迟为20毫秒,vacuum_cost_limit、vacuum_cost_page_dirty和vacuum_cost_page_miss的默认设置在扫描不需要清理的表或索引页时将VACUUM限制在28.8 GB/小时左右,在扫描表或索引页时大约为9.6 GB/小时,所有这些都需要清理。对于大小为数百GB或TB的表,这会使VACUUM时间太长;如果在VACUUM结束之前,表又要VACUUM,它就会膨胀。

此外,VACUUM没有能力根据系统繁忙和空闲期来调整其活动,因为它不知道这些周期是什么时候发生的。理想情况下,空闲的系统将以最快的速度执行正在进行或甚至即将到期的任何VACUUM,然后在系统处于前台负载时进行得更慢。但是,没有调度系统来实现这样的事情;如果您想要这种行为,必须手动配置它。

如果我用一句话来概括以上所有的话,也许是这样的:VACUUM已经走了很长一段路,但它仍然需要监测和管理。小型安装--就像我使用的第一次安装一样--运行当前版本的PostgreSQL可能根本不需要做任何关于VACUUM的事情,而且即使是更大的安装,今天的问题通常也比早期版本的问题要少。同时,对于需要持续良好性能的大型安装,VACUUM通常至少需要一些监视、管理和调优。时间将告诉我们未来版本会带来什么改进。

用户评论: 或者,Postgres可以将行版本存储在数据库之外,比如SQL Server,这根本不需要真空。行版本只是附加到临时数据库中类似日志的结构中。日志是根据最老的活动事务从后面大容量修剪的。这里的主要缺点是长时间运行的事务阻止了日志的收缩。在实践中,这种影响是可以忽略不计的。

转载于:https://my.oschina.net/lvhongqing/blog/1603471

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值