【问题标题】:Postgres EXPLAIN ANALYZE cost estimate row count massively higher than actual row count. No Vacuuming?Postgres EXPLAIN ANALYZE 成本估算行数大大高于实际行数。没有吸尘?
【发布时间】:2018-12-11 08:22:15
【问题描述】:

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

->  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 中找到了这个给出 autovacuum 统计信息的命令,这是输出(不幸的是,我在手动清理之后运行了它。

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

【问题讨论】:

    标签: django postgresql heroku


    【解决方案1】:

    要查看 autovacuum 是否已按应有的方式启用,请运行

    SHOW autovacuum;
    

    要查看是否为您的特定表禁用了 autovacuum,请运行

    SELECT reloptions FROM pg_class WHERE relname = 'listings_listing';
    

    B) 的答案很简单:

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

    A)的答案更难:

    有几件事可以阻止 autovacuum 完成其工作:

    • 此表的更改率可能非常高,以至于默认运行缓慢的 autovacuum 无法跟上正常活动。

      在这种情况下,您应该将 autovacuum 调整为对该表更具侵略性:

      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”。

      当 autovacuum 遇到这样的锁定时,它会退出而不是阻止用户活动。

    确定发生了什么的一种有用方法是像这样查询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 以外的值并偶尔查看日志。

    【讨论】:

    • 谢谢。运行您的命令显示此表的 autovacuum 已打开且未禁用。 pg_stat_user_tables 显示 last_autovacuum 是在 2018-06-27 完成的。我还运行了一个显示 autovacuum_threshold 为 34,757 行的 heroku 命令。所以我不明白为什么它还没有运行。还是与 autovacuum_vacuum_cost_delay 不同?我不在此表上执行长锁。
    • 也许只是批量删除。监控表膨胀,看看会发生什么。
    • 我每 15 分钟运行一次删除陈旧列表的任务,因此不太可能进行批量删除。我将尝试监控膨胀。改变 cost_delay 的想法是否可能是它试图吸尘但它一直在睡觉?我的服务不是那么受欢迎,所以我很难相信。
    • Autovacuum 默认变慢,它经常需要休息。除非你知道有必要,否则不要调整它。
    【解决方案2】:

    Laurenz Albe 的回答非常适合解释自动吸尘的原因,但我现在想回答我后来发现的为什么我的死元组数量激增。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-21
      • 1970-01-01
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多