Postgres EXPLAIN ANALYZE成本估算行数大大高于实际行数。没有吸尘?

时间:2018-07-03 05:53:13

标签: django postgresql heroku

我在Django项目的Heroku上运行一个Postgres 9.4.18数据库。我注意到查询变得越来越慢,因此我对一个查询运行了“ EXPLAIN ANALYZE”,并注意到对于一个节点,行估计大大高于实际行数:

->  Seq Scan on listings_listing u1  (cost=0.00..1536692.01 rows=5030003 width=8) (actual time=0.811..11263.410 rows=173537 loops=1)

然后我在表上运行“ VACUUM FULL ANALYZE”,然后在查询上重新运行“ EXPLAIN ANALYZE”,并得到:

->  Seq Scan on listings_listing u1  (cost=0.00..23554.61 rows=173537 width=8) (actual time=0.001..33.884 rows=173537 loops=1)

现在执行时间快了100倍。

因此,这两个问题是:A)自动吸尘是否可以防止这种情况发生? (如何检查该功能是否已启用?)B)假设未执行吸尘操作,它是如何实现的?

---------------------------------更新

我从heroku中找到了该命令,该命令提供了自动真空统计信息,这是输出(不幸的是,我在手动吸尘后运行了该命令。

heroku pg:vacuum_stats DATABASE_URL

schema |                  table                  | last_vacuum | last_autovacuum  |    rowcount    | dead_rowcount  | autovacuum_threshold | expect_autovacuum 
--------+-----------------------------------------+-------------+------------------+----------------+----------------+----------------------+-------------------
 public | listings_listing                        |             | 2018-06-27 15:36 |        173,537 |              0 |         34,757       | 

似乎所指示的阈值早已导致其真空运行。

此外,这是Heroku页面,提供了有关吸尘设置的文档: https://devcenter.heroku.com/articles/managing-vacuum-on-heroku-postgres

2 个答案:

答案 0 :(得分:4)

要了解是否已启用自动抽真空,请运行

SHOW autovacuum;

要查看您的特定表是否禁用了自动真空,请运行

SELECT reloptions FROM pg_class WHERE relname = 'listings_listing';

B)的答案很简单:

如果没有运行自动清理,则每个UPDATEDELETE都会在表中创建一个“死元组”(或“死行版本”)。除非您手动运行VACUUM,否则它们将永远不会被清除,并且将导致表增长,从而使顺序扫描变得更慢。

答案A更加困难:

有几件事可以阻止自动真空工作:

  • 此表的更改速率可能很高,默认情况下自动运行的速度很慢,因此不会干扰正常活动,因此无法跟上进度。

    在这种情况下,您应该调整自动真空度以使该表更具攻击性:

    ALTER TABLE listings_listing SET (
       autovacuum_vacuum_cost_limit = 1000,
       toast.autovacuum_vacuum_cost_limit = 1000
    );
    

    如果这还不够好,您可以

    ALTER TABLE listings_listing SET (
       autovacuum_vacuum_cost_delay = 0,
       toast.autovacuum_vacuum_cost_delay = 0
    );
    
  • 有并发的长事务。

    Autovacuum只能删除比最旧的正在运行的事务更早的死元组,因此长时间的事务会使它无法执行其工作。

    这个故事还有更多;阅读this blog post

    但是,这也会阻止VACUUM (FULL)正常工作,所以也许这不是您的问题。

  • 该表经常被SHARE UPDATE EXCLUSIVE或更强的锁锁定,例如通过运行“ LOCK listings_listing”。

    当自动清理遇到这种锁定时,它会后退而不是阻止用户活动。

确定正在发生什么情况的一种有用方法是像这样查询pg_stat_user_tables

SELECT n_live_tup, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE relname = 'listings_listing';

但是您运行VACUUM (FULL)后,该证据可能已被破坏。

另一件事是将log_autovacuum_min_duration设置为-1以外的值,并偶尔查看日志。

答案 1 :(得分:0)

劳伦兹·阿尔贝(Laurenz Albe)的答案很好地解释了自动吸尘的原因,但我现在想回答一下后来发现的有关死元组计数为何不断膨胀的信息。

基本上是由于我的代码中的错误,我每15分钟更新一次数据库中的每一行,而不是仅仅匹配过滤器的行。每次更新都会创建一个失效的元组,并且它的膨胀速度如此之快,以至于无法继续进行清除。我花了一段时间才找到错误,因为我只查看代码中的删除,而不是更新,因为我当时(当时还没有意识到)它们也会创建死元组。

修复后,无需更改任何自动吸尘设置。肿胀增加是正常的。