使用索引向后扫描而不是仅索引扫描的PostgresQL查询

时间:2020-07-06 08:43:35

标签: postgresql postgresql-12

我遇到了我无法解释的问题,并且已经搜索了几天,并且尚未发现PostgresQL计划程序导致我的“问题”的原因,导致(相对简单的)查询占用了大量的时间。


让我们从头开始(我试图删除尽可能多的无用信息,以使表看起来毫无意义,但请相信我,不是):

我有以下架构:

CREATE TABLE ct_log (
    ID integer,
    CONSTRAINT ctl_pk
        PRIMARY KEY (ID)
);

CREATE TABLE ct_log_entry (
    CERTIFICATE_ID  bigint      NOT NULL,
    ENTRY_ID        bigint      NOT NULL,
    ENTRY_TIMESTAMP timestamp   NOT NULL,
    CT_LOG_ID       integer     NOT NULL,
    CONSTRAINT ctle_ctl_fk
        FOREIGN KEY (CT_LOG_ID)
        REFERENCES ct_log(ID)
) PARTITION BY RANGE (ENTRY_TIMESTAMP);

-- I will not repeat this one 7 times, but there are partition for each year from 2013-2020:
CREATE TABLE ct_log_entry_2020 PARTITION OF ct_log_entry
    FOR VALUES FROM ('2020-01-01T00:00:00'::timestamp) TO ('2021-01-01T00:00:00'::timestamp);

CREATE INDEX ctle_c ON ct_log_entry (CERTIFICATE_ID);
CREATE INDEX ctle_e ON ct_log_entry (ENTRY_ID);
CREATE INDEX ctle_t ON ct_log_entry (ENTRY_TIMESTAMP);
CREATE INDEX ctle_le ON ct_log_entry (CT_LOG_ID, ENTRY_ID DESC);

(如果您对完整的架构感到好奇:https://github.com/crtsh/certwatch_db/blob/master/sql/create_schema.sql

这是我要运行的查询:

SELECT ctl.ID, latest.entry_id
FROM ct_log ctl
LEFT JOIN LATERAL (
    SELECT coalesce(max(entry_id), -1) entry_id
    FROM ct_log_entry ctle
    WHERE ctle.ct_log_id = ctl.id
) latest ON TRUE;

对于认识https://crt.sh的人来说,这可能看起来很熟悉,因为这确实是来自crt.sh的架构。由于crt.sh提供了public PostgresQL access,使我可以比较自己的服务器和它们自己的服务器之间的查询计划,因此这有点有趣。

这种区别非常明显(:sad_smile :),但我不确定为什么会这样,因为据我所知,我拥有正确的索引非常快,并且与crt.sh服务器具有相同的索引。

看起来我的实例正在使用向后索引扫描,而不是仅对最大的2个分区进行索引扫描。并非总是如此,并且以前它使用与crt.sh实例相同的查询计划执行,但是由于某种原因,它决定停止这样做。

(这是这些表中的数据量,以防在查询计划中不清楚:https://d.bouma.dev/wUjdXJXk1OzF。我看不到crt.sh数据库中的数据量,因为它们没有提供访问各个分区)


现在进入我尝试过的事情清单:

  • ANALYZE ct_log_entry(以及由分区创建的ct_log_entry_*表)
  • VACUUM ANALYZE ct_log_entry(以及由分区创建的ct_log_entry_*表)
  • VACUUM FULL ct_log_entry(以及由分区创建的ct_log_entry_*表)
  • 删除ctle_le索引并重新创建(这一次对我有用,给了我几个小时的性能,直到我导入了更多数据,然后又进行了向后扫描)
  • REINDEX INDEX在每个ctle_le表上的ct_log_entry_*索引
  • SET random_page_cost = x;11.145(根据许多SO解答和博客文章)进行了尝试

我唯一注意到的不同是crt.sh正在运行PostgresQL 12.1,而我正在运行12.3,但据我所知这不会产生任何影响。

在您说“是的,但是您不能在笔记本电脑上运行此数量的数据”之前,我正在运行的服务器是一个专用盒,具有32个可用线程和128GB RAM,并运行8个2TB的RAID 5三星EVO 860驱动器在硬件RAID上运行(是的,我知道如果驱动器发生故障,这是很糟糕的,这是我稍后要处理的另一个问题,但是读取性能应该很好)。我不知道crt.sh在硬件上运行什么,但是由于我只导入了一部分数据,所以这里还没有看到我的硬件问题。

我还使用此处的指南https://pgtune.leopard.in.ua/#/“调整”了我的配置。

很高兴在需要的地方提供更多信息,但希望有人可以指出我的缺陷和/或提供解决问题的方法,并向PostgresQL展示如何使用最佳路径!

0 个答案:

没有答案