【问题标题】:How to speed up query with DISTINCT in PostgreSQL?如何在 PostgreSQL 中使用 DISTINCT 加快查询速度?
【发布时间】:2018-12-06 03:33:40
【问题描述】:

如你所见,我有非常简单的 SQL 语句:

SELECT DISTINCT("CITY" || ' | '  || "AREA" || ' | ' || "REGION") AS LOCATION
FROM youtube

我在查询中使用的 youtube 表有大约 2500 万条记录。查询需要很长时间才能完成(约 25 秒)。我正在尝试加快请求速度。

我创建了一个如下所示的索引,但我的更高查询仍然需要相同的时间才能完成。我做错什么了?顺便说一句,在我的情况下使用“分区”更好吗?

CREATE INDEX location_index ON youtube ("CITY", "AREA", "REGION")

EXPLAIN 返回:

Unique (cost=5984116.71..6111107.27 rows=96410 width=32)
-> Sort (cost=5984116.71..6047611.99 rows=25398112 width=32)
   Sort Key: ((((("CITY" || ' | '::text) || "AREA") || ' | '::text) || "REGION"))
   -> Seq Scan on youtube (cost=0.00..1037365.24 rows=25398112 width=32) 

@george-joseph QUERY PLAN 您的脚本:

【问题讨论】:

  • 你可以试试这个查询:select concat(city, '|', area, '|', region) as location from (select city, area, region, count(*) youtube group by city, area, region) x;?这需要多长时间?
  • @zedfoxus 您的查询大约需要 10-12 秒才能完成。
  • 太棒了。这将时间缩短了一半。如果您希望它比这更快,您可能需要考虑物化视图 (postgresqltutorial.com/postgresql-materialized-views) 并通过计划任务/cron 作业定期刷新它。
  • 正如我之前所说的 youtube 我使用的表有大约 2500 万条记录。每 5 分钟将新数据加载到表中。也许最好在表中创建indexpartition?我的主要问题是关于那个。你怎么看?
  • 你可以试试。很难确定分区是否能解决您的问题。如果您不经常运行此类查询并且用户可以等待 10 秒,则无需进行任何更改。如果您的用户不能等待那么久,请每小时将结果缓存到物化视图中。您的查询没有 where 子句,所以我认为分区对您没有帮助。

标签: sql postgresql indexing distinct


【解决方案1】:

索引和分区都无法帮助您。

由于cityarearegion(可能)密切相关,因此结果行数将比 PostgreSQL 估计的要少得多,因为它假定列是相互独立的。

因此,您应该在这些列上创建扩展统计信息,这是 PostgreSQL v10 中引入的一项新功能:

CREATE STATISTICS youtube_stats (ndistinct)
   ON "CITY", "AREA", "REGION" FROM youtube;

ANALYZE youtube;

现在 PostgreSQL 可以更好地了解有多少不同的组。

然后为查询提供大量内存,以便它可以将所有这些组的哈希值放入内存。然后它可以使用哈希聚合而不是对行进行排序:

SET work_mem = '1GB';

您可能不需要那么多内存;尝试找到更合理的限制。

然后尝试 George Joseph 的回答中的查询:

SELECT x."CITY" || ' | '  || x."AREA" || ' | ' || x."REGION" AS location
FROM (SELECT DISTINCT "CITY", "AREA", "REGION"
      FROM youtube) AS x;

【讨论】:

  • 我想你应该提到这个功能只能在 postgres 10 或更高版本上使用..
  • @dwir182 听到就是服从。
  • Since city, area and region are (probably) closely correlated, 它们也可能是低基数 fat 宽的列。 IOW:OP 应该首先标准化他的数据。 (或使用电子表格)
  • 感谢您的指导! =) 在你回答之后,我的查询变得更快(~ 5 秒)。我想我需要了解更多关于STATISTICS 的信息,并且我想我需要升级我的数据库。作为变体,它可以是max_worker_processesmax_parallel_workers_per_gather。你觉得怎么样?
  • 并行化只会有助于顺序扫描,但这并没有随着我的查询而改变(你说这需要 5 秒)。其余时间都在排序,这不能从并行化中受益。一般来说,在数据库中,做正确的事情慢一点通常比快速做错的事情快。
【解决方案2】:

由于您在列上获得了索引, 如果您按以下方式进行,查询计划会是什么样子

SELECT x.city || ' | '  || x.area || ' | ' || x.region
FROM (SELECT DISTINCT city, area, region
      FROM youtube) x 

【讨论】:

  • QUERY PLAN 我的查询您可以在帖子中看到。请再检查一遍。我多次运行您的代码。您的查询需要大约 30 秒才能完成,这比我的要长。你有什么想法吗?
  • 你能分享一下我建议的查询的查询计划吗
  • 请再次查看我的帖子。我在你的脚本中添加了QUERY PLAN
  • 谢谢您,您是否已经在表上运行了分析命令并更新表上的统计信息以查看是否有差异?字段城市、区域和区域也可以为空
【解决方案3】:

索引应该能够提供帮助。尝试将查询编写为:

SELECT DISTINCT ON (city, area, region) "CITY" || ' | '  || "AREA" || ' | ' || "REGION") AS LOCATION
FROM youtube
ORDER BY city, area, region;

这可以利用(city, area, region) 上的索引。

【讨论】:

    猜你喜欢
    • 2014-10-16
    • 2014-10-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 2015-04-07
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多