【问题标题】:PostgreSQL 11.5 doing sequential scan for SELECT EXISTS queryPostgreSQL 11.5 对 SELECT EXISTS 查询进行顺序扫描
【发布时间】:2020-08-24 11:56:51
【问题描述】:

我有一个多租户环境,其中每个租户(客户)都有自己的架构来隔离他们的数据。我知道这并不理想,但它是旧系统的快速移植。

每个租户都有一个“阅读”表,具有 4 列的复合索引: site_code char(8)、location_no int、sensor_no int、reading_dtm timestamptz。

当添加新读数时,会调用一个函数,该函数首先检查是否在最后一分钟已经有读数(对于相同的 site_code.location_no.sensor_no):

   IF EXISTS (
        SELECT
            FROM reading r
            WHERE r.site_code   = p_site_code
            AND   r.location_no = p_location_no
            AND   r.sensor_no   = p_sensor_no
            AND   r.reading_dtm > p_reading_dtm - INTERVAL '1 minute'
    )
    THEN
        RETURN;
    END IF;

现在,请记住,有许多租户,除了 1 之外,所有租户都表现良好。在其中 1 个租户中,调用花费了近半秒而不是通常的几毫秒,因为它正在对一张表进行顺序扫描近 200 万行而不是索引扫描。

我的 random_page_cost 设置为 1.5。

如果查询可能返回很多行,我可以理解顺序扫描,检查是否存在。

我已经在桌子上尝试过 ANALYZE、VACUUM FULL 等,但没有任何区别。

如果我在查询前加上“SET LOCAL enable_seqscan = off”,它工作得很好......但感觉不对,但它必须是一个临时解决方案,因为这是一个实时系统,它需要工作。

我还能做些什么来帮助 Postgres 做出使用索引的更好决定?

编辑:如果我手动(在函数之外)执行类似的查询,它会选择一个索引。

【问题讨论】:

  • 由于某种原因,PostgreSQL 认为谓词 r.reading_dtm > p_reading_dtm - INTERVAL '1 minute' 的选择性不够。
  • 我想知道这是否与函数内部缓存的执行计划有关
  • 可能是 - 如果我手动模仿查询,它会选择索引扫描。

标签: postgresql


【解决方案1】:

我的猜测是引擎正在评估谓词并认为它的选择性不够(认为会返回太多行),因此决定改用表扫描。

我会做两件事:

  • 确保您有正确的索引:

     create index ix1 on reading (site_code, location_no, 
                                  sensor_no, reading_dtm);
    
  • 通过使选择性看起来更好来欺骗优化器。您可以通过添加额外的 [冗余] 谓词 and r.reading_dtm < :p_reading_dtm 来做到这一点:

     select 1
     from reading r
     where r.site_code   = :p_site_code
       and r.location_no = :p_location_no
       and r.sensor_no   = :p_sensor_no
       and r.reading_dtm > :p_reading_dtm - interval '1 minute'
       and r.reading_dtm < :p_reading_dtm
    

【讨论】:

  • 是的,选项 2 似乎已修复它。我记得以前做过类似的事情,但不确定它为什么会起作用。这绝对是一个记录,因为我想任何人都会想知道为什么......
猜你喜欢
  • 2015-08-09
  • 1970-01-01
  • 2021-05-23
  • 2011-07-09
  • 2023-04-03
  • 2017-10-22
  • 1970-01-01
  • 2016-11-01
  • 2020-12-22
相关资源
最近更新 更多