Postgresql 如果我使用另一个ORDER BY,为什么postgres不再使用索引?
我在这里没有找到我的问题的答案,所以我想我会问: 下表中有大约18kk行: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
# 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的了解!我有时间会去看看的。现在,我将只使用多列索引。再次感谢你的回答!