Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PostgreSQL未在筛选的多排序查询上使用索引_Sql_Postgresql_Sorting_Indexing_Postgresql Performance - Fatal编程技术网

PostgreSQL未在筛选的多排序查询上使用索引

PostgreSQL未在筛选的多排序查询上使用索引,sql,postgresql,sorting,indexing,postgresql-performance,Sql,Postgresql,Sorting,Indexing,Postgresql Performance,我有一张非常简单的桌子 CREATE TABLE approved_posts ( project_id INTEGER, feed_id INTEGER, post_id INTEGER, approved_time TIMESTAMP NOT NULL, post_time TIMESTAMP NOT NULL, PRIMARY KEY (project_id, feed_id, post_id) ) 我正在尝试优化此查询: SELECT * FROM approv

我有一张非常简单的桌子

CREATE TABLE approved_posts (
  project_id INTEGER,
  feed_id INTEGER,
  post_id INTEGER,
  approved_time TIMESTAMP NOT NULL,
  post_time TIMESTAMP NOT NULL,
  PRIMARY KEY (project_id, feed_id, post_id)
)
我正在尝试优化此查询:

SELECT *
FROM approved_posts
WHERE feed_id IN (?, ?, ?)
AND project_id = ?
ORDER BY approved_time DESC, post_time DESC
LIMIT 1;
查询优化器将获取与谓词匹配的每一个
approved\u post
,对所有100k个结果进行排序,并返回找到的前一个结果

我确实有一个关于
项目id、提要id、批准时间、发布时间的索引,如果我:
A.
post\u时间删除排序
,或
B.用单个
=?
替换(?,?)
中的

然后,它只需进行反向索引扫描即可获得第一个结果,并且速度极快

选项A:

 Limit  (cost=0.43..6.57 rows=1 width=24) (actual time=0.101..0.101 rows=1 loops=1)
   ->  Index Scan Backward using approved_posts_approved_time_idx on approved_posts p  (cost=0.43..840483.02 rows=136940 width=24) (actual time=0.100..0.100 rows=1 loops=1)
     Filter: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
     Rows Removed by Filter: 37
 Total runtime: 0.129 ms
Limit  (cost=0.43..3.31 rows=1 width=24) (actual time=0.065..0.065 rows=1 loops=1)
   ->  Index Scan Backward using approved_posts_full_pagination_index on approved_posts p  (cost=0.43..126884.70 rows=44049 width=24) (actual time=0.063..0.063 rows=1 loops=1)
     Index Cond: ((project_id = 148772) AND (feed_id = 73321))
 Total runtime: 0.092 ms
选项B:

 Limit  (cost=0.43..6.57 rows=1 width=24) (actual time=0.101..0.101 rows=1 loops=1)
   ->  Index Scan Backward using approved_posts_approved_time_idx on approved_posts p  (cost=0.43..840483.02 rows=136940 width=24) (actual time=0.100..0.100 rows=1 loops=1)
     Filter: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
     Rows Removed by Filter: 37
 Total runtime: 0.129 ms
Limit  (cost=0.43..3.31 rows=1 width=24) (actual time=0.065..0.065 rows=1 loops=1)
   ->  Index Scan Backward using approved_posts_full_pagination_index on approved_posts p  (cost=0.43..126884.70 rows=44049 width=24) (actual time=0.063..0.063 rows=1 loops=1)
     Index Cond: ((project_id = 148772) AND (feed_id = 73321))
 Total runtime: 0.092 ms
但是如果没有这些调整,它的性能就不那么好

Limit  (cost=169792.16..169792.17 rows=1 width=24) (actual time=510.225..510.225 rows=1 loops=1)
   ->  Sort  (cost=169792.16..170118.06 rows=130357 width=24) (actual time=510.224..510.224 rows=1 loops=1)
     Sort Key: approved_time, post_time
     Sort Method: top-N heapsort  Memory: 25kB
     ->  Bitmap Heap Scan on approved_posts p  (cost=12324.41..169140.38 rows=130357 width=24) (actual time=362.210..469.387 rows=126260 loops=1)
           Recheck Cond: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
           ->  Bitmap Index Scan on approved_posts_feed_id_idx  (cost=0.00..12291.82 rows=130357 width=0) (actual time=354.496..354.496 rows=126260 loops=1)
                 Index Cond: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
Total runtime: 510.265 ms
我甚至可以在这5个提要ID上添加一个条件索引,它将再次做正确的事情

我目前最好的解决方案是将每个
feed\u id
放在自己的查询中,并在它们之间进行大规模的
联合。但这并不能很好地扩展,因为我可能想从30个提要中选择前500个提要,拉入15k行并对它们进行排序。使用此策略管理补偿也有点复杂

有人知道我如何在
子句中对索引良好的数据进行两种排序,并让Postgres做正确的事情吗

我正在使用Postgres9.3.3。以下是我的索引

 "approved_posts_project_id_feed_id_post_id_key" UNIQUE CONSTRAINT, btree (project_id, feed_id, post_id)
 "approved_posts_approved_time_idx" btree (approved_time)
 "approved_posts_feed_id_idx" btree (feed_id)
 "approved_posts_full_pagination_index" btree (project_id, feed_id, approved_time, post_time)
 "approved_posts_post_id_idx" btree (post_id)
 "approved_posts_post_time_idx" btree (post_time)
 "approved_posts_project_id_idx" btree (project_id)
-> Index Scan Backward using approved_posts_approved_time_idx on approved_posts p (cost=0.43..840483.02 rows=136940 width=24) (actual time=0.100..0.100 rows=1 loops=1) Filter: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
"approved_posts_foo_idx" btree (project_id, approved_time DESC, post_time DESC)
所有列都不能为空

此表有2m行,分为200个提要ID和19个项目ID

以下是最常见的提要ID:

 feed_id | count  
---------+--------
   73607 | 558860
   73837 | 354018
   73832 | 220285
   73836 | 172664
   73321 | 118695
   73819 |  95999
   73821 |  75871
   73056 |  65779
   73070 |  54655
   73827 |  43710
   73079 |  36700
   73574 |  36111
   73055 |  25682
   73072 |  22596
   73589 |  19856
   73953 |  15286
   73159 |  13059
   73839 |   8925
就每个
feedid
/
projectid
配对的最小/最大/平均基数而言,我们有:

 min |  max   |          avg          
-----+--------+-----------------------
   1 | 559021 | 9427.9140271493212670

据我所知,如果第一个“where”不是密钥的第一部分,则不会使用密钥。尝试将查询中“where's”的顺序切换为project\u id和feed\u id。

对于
feed\u id
,Postgres很难找到最佳的查询计划。每个
feed\u id
可以与1-559021行关联(根据您的数字)。Postgres目前还不够聪明,无法单独看到
LIMIT 1
特例的潜在优化。一个由多个查询组成的
UNION ALL
(不仅仅是
UNION
),每个查询都有一个
feed\u id
LIMIT 1
,再加上另一个外部
LIMIT 1
(就像您似乎已经尝试过的那样)展示了这一潜力,但需要复杂的查询串联以获得数量可变的输入值

还有另一种方法可以说服查询计划员,它可以使用索引扫描从索引中为每个
提要id
选择第一行:使用
横向连接重写查询:

SELECT a.*
FROM   (VALUES (?), (?), (?)) AS t(feed_id)
     , LATERAL (
   SELECT *
   FROM   approved_posts
   WHERE  project_id = ?
   AND    feed_id = t.feed_id
   ORDER  BY approved_time DESC, post_time DESC
   LIMIT  1
   ) a
ORDER  BY approved_time DESC, post_time DESC
LIMIT  1;
或者,对于
feed\u id
的可变数量的值更方便:

SELECT a.*
FROM   unnest(?) AS t(feed_id)  -- provide int[] var
     , LATERAL ( ...
为变量传递一个整数数组,如
“{123,234,345}”::int[]
。这也可以通过使用变量参数的函数优雅地实现。然后,您可以传递
整数
值列表:

你在
(project\u id,feed\u id,approved\u time,post\u time)上的索引可以实现这一点,因为Postgres可以将索引向后扫描几乎和向前扫描一样快,但是
(project\u id,feed\u id,approved\u time DESC,post\u time DESC)
会更好。见:

如果不需要返回表中的所有列,甚至可以选择仅索引扫描

您的列
approved\u time
post\u time
已定义
非空
。否则,您必须做更多的工作:

详细说明横向连接技术的相关答案:

你的选择A为什么奏效? 仔细观察会发现两件事:

 "approved_posts_project_id_feed_id_post_id_key" UNIQUE CONSTRAINT, btree (project_id, feed_id, post_id)
 "approved_posts_approved_time_idx" btree (approved_time)
 "approved_posts_feed_id_idx" btree (feed_id)
 "approved_posts_full_pagination_index" btree (project_id, feed_id, approved_time, post_time)
 "approved_posts_post_id_idx" btree (post_id)
 "approved_posts_post_time_idx" btree (post_time)
 "approved_posts_project_id_idx" btree (project_id)
-> Index Scan Backward using approved_posts_approved_time_idx on approved_posts p (cost=0.43..840483.02 rows=136940 width=24) (actual time=0.100..0.100 rows=1 loops=1) Filter: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
"approved_posts_foo_idx" btree (project_id, approved_time DESC, post_time DESC)
有选择地增加列
project\u id
feed\u id
的统计目标可能会有好处,因此可以更准确地估计两种策略之间的转折点


由于您的项目只有旧的行(),因此可以通过提示每个项目(和/或每个
提要id
)的
批准的\u时间的最大值来改进此查询,或者至少是一个上限

SELECT ...
WHERE  ...
AND   approved_time <= $upper_bound
选择。。。
哪里

时间还没到呢!谢谢你。我一直在将DESCs交换到ASCs和posttime/approvedtime,但没有想到交换WHERE条件。绝对值得一试<代码>9.3.3
回避了一个问题:为什么不至少9.3.9(如果9.4不是一个选项)?。我们将根据您的建议进行升级。您提供了所有必要的详细信息,这使我能够找到您感兴趣问题的答案。许多问题无法提供基本信息,这在这里是一个经常令人讨厌的问题,现在让你的问题在这方面大放异彩。这是目前教postgres使用哪个索引的最优雅的方式,在我们的查询生成器中更容易适应!很高兴知道,巨大的值范围是postgres出错的地方。今天早上我遇到了一个问题:如果根本的问题是每个提要id只有1个条目,那么为什么我们放弃二次排序(并且只按批准的时间描述排序)它选择反向索引扫描而不需要任何其他更改?编辑:事实上,想想看,因为按批准的时间排序只会对(批准的时间、发布的时间)进行反向索引扫描,所以它实际上已经以批准的时间描述、发布的时间描述顺序返回数据。一旦我们简单地按照博士后已经给我们的顺序要求它,为什么世界上的博士后会改变它的计划?@MikeFairhurst:好问题,我对这个转折点感到困惑