加速 SQL 子查询

Speed up SQL subquery

我正在开发一个系统,任何数量的公司都可以将中小企业信息上传到数据库中并进行搜索。

table import_data 存储由不同公司上传的中小企业信息。 各公司上传的信息由tableimport_data.

company_id标识
CREATE TABLE import_data 
(
    id uuid NOT NULL,    # Indexed
    company_id integer NOT NULL,    # Indexed
    business_name character varying,    # Indexed
    no_of_rating double precision,    # Indexed
    no_of_reviews double precision,    # Indexed
    phone character varying,    # Indexed
    email character varying,    # Indexed
    address character varying,    # Indexed
    upserted_by integer,  # Not indexed
    upsert_date timestamp without time zone,    # Not indexed
)
PARTITION BY LIST (company_id);

现在我在做一个推荐系统,其中phone号的记录不存在于特定公司上传的数据中,但存在于另一家公司上传的数据中,推荐给第一家公司购买。 公司与其拥有的所有 import_data 记录之间的关系存储在另一个 table import_data_company_rel 中。 table 包括由特定公司上传和购买的所有 import_data 记录。

CREATE TABLE import_data_company_rel 
(
    record_id uuid NOT NULL,    # Indexed
    company_id integer NOT NULL    # Indexed
)
PARTITION BY LIST (company_id);

两个 table 都进行了分区,以便每家公司将其数据存储在自己的分区中。

例如,id 为 1 的公司将其数据存储在 import_data_1,import_data_company_rel_1,id 为 2 的公司将其数据存储在 import_data_2 , import_data_company_rel_2 具有与 tables import_data 和 import_data_company_rel.

相同的模式

这是 SQL 查询,用于获取特定公司的推荐记录,假设 ID 为 1。

SELECT id,business_name,address
FROM import_data 
INNER JOIN import_data_company_rel idcr ON import_data.id = idcr.record_id
WHERE idcr.company_id != 1 
  AND import_data.phone NOT IN (SELECT import_data.phone 
                                FROM import_data 
                                INNER JOIN import_data_company_rel idcr
                                      ON import_data.id = idcr.record_id 
                                WHERE idcr.company_id = 1)
LIMIT 100 OFFSET 0;

该查询获取第一页推荐结果,phone条记录未包含在公司1的记录中

虽然这适用于少量记录,但此查询不能很好地扩展记录数。

百万条记录时,100条记录连一个小时都执行不完

我发现查询中减慢执行速度的部分是子查询

SELECT import_data.phone 
FROM import_data 
INNER JOIN import_data_company_rel idcr
      ON import_data.id = idcr.record_id 
WHERE idcr.company_id = 1

它选择所有已由 ID 为 1 的公司拥有的 phone 号码,以从推荐中过滤。此查询将逐行扫描整个 table(s)。

我认为分区 table 与慢速查询没有任何关系,因为即使没有分区查询也很慢。

explain (analyze, buffers, format text)

结果可见https://explain.depesz.com/s/MBUL。 我正在研究 postgesql v13.2

如何加快查询速度?有可能吗?任何帮助表示赞赏。谢谢。

您可以做两件事来提高性能:

  1. 通过

    提高索引扫描的速度
    VACUUM import_data_company_rel_1;
    

    这将摆脱昂贵的堆获取。

  2. 重写您的查询以使用 NOT EXISTS:

    import_data.phone NOT IN
        (SELECT import_data.phone
         FROM import_data
            INNER JOIN import_data_company_rel idcr
               ON import_data.id = idcr.record_id
         WHERE idcr.company_id = 1)
    

    可以重写为

    NOT EXISTS
        (SELECT 1
         FROM import_data
            INNER JOIN import_data_company_rel idcr
               ON import_data.id = idcr.record_id
         WHERE idcr.company_id = 1
           AND import_data.phone = import_data.phone)