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

jef*_*fjv 6 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)
Run Code Online (Sandbox Code Playgroud)

然后我在桌面上运行"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)
Run Code Online (Sandbox Code Playgroud)

现在执行时间快了100倍.

所以这两个问题是:A)不应该自动吸尘是否会阻止这种情况?(如何检查是否已启用?)B)假设未执行抽真空,它是如何实现的?

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

我从heroku发现了这个命令,它给出了autovacuum stats,这里是输出(不幸的是我在手动真空后运行它.

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       | 
Run Code Online (Sandbox Code Playgroud)

似乎所指示的阈值应该导致它在很久以前运行真空.

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

Lau*_*lbe 5

要查明是否已启用自动抽真空,请运行

SHOW autovacuum;
Run Code Online (Sandbox Code Playgroud)

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

SELECT reloptions FROM pg_class WHERE relname = 'listings_listing';
Run Code Online (Sandbox Code Playgroud)

B)的答案很简单:

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

A)的答案更加困难:

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

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

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

    ALTER TABLE listings_listing SET (
       autovacuum_vacuum_cost_limit = 1000,
       toast.autovacuum_vacuum_cost_limit = 1000
    );
    
    Run Code Online (Sandbox Code Playgroud)

    如果那还不够好,您可以

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

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

    故事还有更多;阅读此博客文章

    但是,这也将使其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';
Run Code Online (Sandbox Code Playgroud)

但是,既然您已经逃跑了,那证据可能就被摧毁了VACUUM (FULL)

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