Postgresql 如果我使用另一个ORDER BY,为什么postgres不再使用索引?

Postgresql 如果我使用另一个ORDER BY,为什么postgres不再使用索引?,postgresql,indexing,sql-order-by,Postgresql,Indexing,Sql Order By,我在这里没有找到我的问题的答案,所以我想我会问: 下表中有大约18kk行: # SELECT COUNT(1) from report; count ---------- 18090892 (1 row) # \d report Table "public.report" Column | Type | Collation | Nullable | Defau

我在这里没有找到我的问题的答案,所以我想我会问:

下表中有大约18kk行:

# SELECT COUNT(1) from report;
  count   
----------
 18090892
(1 row)

# \d report
                          Table "public.report"
   Column    |           Type           | Collation | Nullable | Default 
-------------+--------------------------+-----------+----------+---------
 reporter_id | uuid                     |           | not null | 
 parsed      | boolean                  |           | not null | 
 id          | text                     |           | not null | 
 request_id  | uuid                     |           |          | 
 created     | timestamp with time zone |           | not null | now()
 customer    | text                     |           | not null | 
 subject     | text                     |           |          | 
Indexes:
    "PK_99e4d0bea58cba73c57f935a546" PRIMARY KEY, btree (id)
    "idx_report_created_desc" btree (created DESC)
    "idx_report_reporter_id_asc_created_desc" btree (reporter_id, created DESC)
    "idx_report_request_id_asc_created_desc" btree (request_id, created DESC)
Foreign-key constraints:
    "FK_5b809608bb38d119333b69f65f9" FOREIGN KEY (request_id) REFERENCES request(id)
    "FK_d41df66b60944992386ed47cf2e" FOREIGN KEY (reporter_id) REFERENCES reporter(id)
如果我使用创建的描述限制25的
订单
则使用索引:

# EXPLAIN ANALYZE SELECT * FROM report ORDER BY created DESC LIMIT 25;
                                                                      QUERY PLAN                          
                                            
----------------------------------------------------------------------------------------------------------
--------------------------------------------
 Limit  (cost=0.44..2.49 rows=25 width=169) (actual time=0.035..0.063 rows=25 loops=1)
   ->  Index Scan using idx_report_created_desc on report  (cost=0.44..1482912.16 rows=18090892 width=169)
 (actual time=0.033..0.051 rows=25 loops=1)
 Planning Time: 0.239 ms
 Execution Time: 0.105 ms
(4 rows)
但是,如果我使用创建的描述、id ASC LIMIT 25的顺序,则不再使用索引:

# EXPLAIN ANALYZE SELECT * FROM "report" ORDER BY "created" DESC, "id" ASC LIMIT 25;
                                                                   QUERY PLAN                                                                    
-------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=587891.07..587893.99 rows=25 width=169) (actual time=2719.606..2726.355 rows=25 loops=1)
   ->  Gather Merge  (cost=587891.07..2346850.67 rows=15075744 width=169) (actual time=2711.873..2718.618 rows=25 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Sort  (cost=586891.04..605735.72 rows=7537872 width=169) (actual time=2643.445..2643.448 rows=21 loops=3)
               Sort Key: created DESC, id
               Sort Method: top-N heapsort  Memory: 35kB
               Worker 0:  Sort Method: top-N heapsort  Memory: 32kB
               Worker 1:  Sort Method: top-N heapsort  Memory: 31kB
               ->  Parallel Seq Scan on report  (cost=0.00..374177.72 rows=7537872 width=169) (actual time=0.018..1910.204 rows=6030297 loops=3)
 Planning Time: 0.396 ms
 JIT:
   Functions: 1
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 4.757 ms, Inlining 0.172 ms, Optimization 5.053 ms, Emission 2.003 ms, Total 11.985 ms
 Execution Time: 2731.226 ms
(16 rows)
如果我理解正确的话,仍然应该使用索引,因为应该返回相同的结果集,只可能是按照id ASC所确定的不同顺序返回

所以我想知道为什么postgres决定做一个并行的seq扫描,而不是使用索引,然后根据它们的id对返回的25行进行排序?这肯定比并行序列扫描快,不是吗


或者我错在哪里?

这里最有可能的解释是,您目前只有
创建的DESC
上的索引。因此,在执行以下查询时:

按创建的描述id从报告顺序中选择*
创建的单列
索引的叶节点没有可用的
id
值。如果Postgres要使用此索引,它必须为每个叶节点返回原始表(聚集索引)。这种来回搜索可能代价高昂,有时甚至会超过最初使用索引的好处

如果需要这样的两层排序,请添加一个涵盖该排序的索引:

在报告上新建索引idx_(已创建描述,id);

请注意,假设
id
是该表的主键,则某些数据库(例如MySQL)会在
id
列上自动标记到当前
created DESC
索引。但在Postgres中似乎并非如此。

PostgreSQL并不是无限聪明的。有些事情是无法解决的,即使理论上可以解决

但它正变得越来越聪明。升级到13版,看看会发生什么。它应该使用索引扫描加上非常快速的“增量排序”。增量排序只需要打破“已创建”之间的联系,我认为这将是罕见的

如果我理解正确的话,仍然应该使用索引,因为应该返回相同的结果集,只可能以由order by id ASC确定的不同顺序返回


但在存在限制的情况下,以不同的顺序返回结果意味着返回不同结果的可能性。因此,它必须采取特殊措施来处理这一问题。是的,正如在
\d报告的输出中所看到的,我只在
(创建的描述)
上有一个相关的索引,在
(创建的描述,id)
上没有索引。但问题是为什么我甚至需要一个多列索引,因为我理解这个查询,它应该能够在创建时使用单列索引。你接受的answe并没有真正回答你的问题。为什么不呢?我问它为什么在我的例子中不使用单列索引,答案是因为postgres还不支持它(“postgres不是无限智能”)。至少不是我正在使用的v12.6。当我有时间的时候,我会查看v13。@ekzyis你可以问任何数据库的问题,我的回答也同样有效。您的问题的答案是,您拥有的单列B树索引对
id
列一无所知。这使得索引对于两步排序没有多大帮助。也许Postgres有一些即将推出的增强功能。你接受的答案基本上是“Postgres不使用索引,因为它不使用索引。”啊,好的,有道理,谢谢你对v13的了解!我有时间会去看看的。现在,我将只使用多列索引。再次感谢你的回答!