Postgres SLOWER设置了LIMIT:除了添加虚拟`ORDER BY`之外,如何解决?

问题描述 投票:0回答:1

在Postgres中,添加LIMIT时,某些查询要慢得多:

查询:

SELECT * FROM review WHERE clicker_id=28 ORDER BY done DESC LIMIT 4; -- 51 sec
SELECT * FROM review WHERE clicker_id=28 ORDER BY id, done DESC LIMIT 4; -- 0.020s
SELECT * FROM review WHERE clicker_id=28 LIMIT 4; -- 0.007s
SELECT * FROM review WHERE clicker_id=28 ORDER BY id; -- 0.007s

您可以看到,我需要在ORDER BY上添加一个虚拟ID,以便快速运行。我试图了解原因。

在它们上运行EXPLAIN

EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY done DESC LIMIT 4;
EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY id, done DESC LIMIT 4;
EXPLAIN SELECT * FROM review WHERE clicker_id=28 LIMIT 4;
EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY id;

给出答案:

EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY done DESC LIMIT 4
Limit  (cost=0.44..249.76 rows=4 width=56)
  ->  Index Scan using review_done on review  (cost=0.44..913081.13 rows=14649 width=56)
        Filter: (clicker_id = 28)


EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY id, done DESC LIMIT 4
Limit  (cost=11970.75..11970.76 rows=4 width=56)
  ->  Sort  (cost=11970.75..12007.37 rows=14649 width=56)
        Sort Key: id, done DESC
        ->  Index Scan using review_clicker_id on review  (cost=0.44..11751.01 rows=14649 width=56)
              Index Cond: (clicker_id = 28)


EXPLAIN SELECT * FROM review WHERE clicker_id=28 LIMIT 4
Limit  (cost=0.44..3.65 rows=4 width=56)
  ->  Index Scan using review_clicker_id on review  (cost=0.44..11751.01 rows=14649 width=56)
        Index Cond: (clicker_id = 28)


EXPLAIN SELECT * FROM review WHERE clicker_id=28 ORDER BY id
Sort  (cost=12764.61..12801.24 rows=14649 width=56)
  Sort Key: id
  ->  Index Scan using review_clicker_id on review  (cost=0.44..11751.01 rows=14649 width=56)
        Index Cond: (clicker_id = 28)

我不是SQL专家,但我认为Postgres期望查询比实际速度快,因此使用一种方法来获取实际上不合适的数据,对吗?

数据库:

  • review表:
    • 包含22+百万行。
      • 给定的用户将获得7 066行的顶部。
      • 测试中的那个(id 28)当时有288。
    • 具有以下结构:
      • id:bigint自动递增[nextval('public.review_id_seq')]] >>
      • 类型:review_type NULL
      • iteration:smallint NULL
      • 重复:smallint NULL
      • 由于:timestamptz为NULL
      • done:timestamptz NULL
      • 添加:timestamptz NULL
      • clicker_id:bigint NULL
      • monologue_id:bigint NULL
  • 具有以下索引:
    • 唯一类型,clicker_id,monologue_id,迭代
    • INDEX clicker_id
    • INDEX已完成,到期,monologue_id
    • 索引ID
    • 索引已完成DESC
    • 索引类型

其他详细信息:

环境:

  • 这些查询是在Postgres 9.6.14开发中运行的。
  • 将查询运行到生产环境中(Heroku Postgres,版本9.6.16),差别不大,但仍然不是很大:缓慢的查询可能需要600毫秒。
  • 变速:

  • 有时,相同的查询(无论是完全相同还是针对不同的clicker_id)的运行速度要快得多(不到1秒),但我不明白为什么。我需要它们一致地快速。
  • 如果我对有288行的用户使用LIMIT 288,那么它会快得多(<1秒),但是如果我对有7066行的用户执行相同的操作,那它会变得非常慢。
  • 在我想到使用虚拟ORDER BY之前,我尝试了这些:

问题:

我的问题本身已经解决,但是我对此不满意:

  • 是否存在此“模式”的名称,其中包括添加虚拟ORDER BY以加快速度?将来
  • 我该如何发现
  • 这样的问题? (这花了很多年才能弄清楚。)除非我错过了什么,否则EXPLAIN没那么有用:
    • 对于慢查询,成本误导性慢,而对于快速变体,误导性高。
  • 替代:还有另一种方法来处理此问题吗?
  • (因为这种解决方案感觉像是黑客。)

    谢谢!


类似问题:

在Postgres中,添加LIMIT时,某些查询要慢得多:查询:SELECT * FROM review WHERE clicker_id = 28 ORDER BY done DESC LIMIT 4; -51秒SELECT * FROM review WHERE ...

postgresql sql-order-by sql-execution-plan explain
1个回答
0
投票

这里的根本问题是所谓的提前中止查询计划。这是一个来自pgsql-hackers的线程,描述了类似的内容:

© www.soinside.com 2019 - 2024. All rights reserved.