[英]PostgreSQL: Limit in query makes it not use index
我有一个带有 BRIN 索引的大表,如果我用限制查询它会忽略索引和 go 进行序列扫描,没有它使用索引(我尝试了几次,结果相同)
explain (analyze,verbose,buffers,timing,costs)
select *
from testj.cdc_s5_gpps_ind
where id_transformace = 1293
limit 100
Limit (cost=0.00..349.26 rows=100 width=207) (actual time=28927.179..28927.214 rows=100 loops=1)
Output: id, date_key_trainjr...
Buffers: shared hit=225 read=1680241
-> Seq Scan on testj.cdc_s5_gpps_ind (cost=0.00..3894204.10 rows=1114998 width=207) (actual time=28927.175..28927.202 rows=100 loops=1)
Output: id, date_key_trainjr...
Filter: (cdc_s5_gpps_ind.id_transformace = 1293)
Rows Removed by Filter: 59204140
Buffers: shared hit=225 read=1680241
Planning Time: 0.149 ms
Execution Time: 28927.255 ms
explain (analyze,verbose,buffers,timing,costs)
select *
from testj.cdc_s5_gpps_ind
where id_transformace = 1293
Bitmap Heap Scan on testj.cdc_s5_gpps_ind (cost=324.36..979783.34 rows=1114998 width=207) (actual time=110.103..467.008 rows=1073725 loops=1)
Output: id, date_key_trainjr...
Recheck Cond: (cdc_s5_gpps_ind.id_transformace = 1293)
Rows Removed by Index Recheck: 11663
Heap Blocks: lossy=32000
Buffers: shared hit=32056
-> Bitmap Index Scan on gpps_brin_index (cost=0.00..45.61 rows=1120373 width=0) (actual time=2.326..2.326 rows=320000 loops=1)
Index Cond: (cdc_s5_gpps_ind.id_transformace = 1293)
Buffers: shared hit=56
Planning Time: 1.343 ms
JIT:
Functions: 2
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 0.540 ms, Inlining 32.246 ms, Optimization 44.423 ms, Emission 22.524 ms, Total 99.732 ms
Execution Time: 537.627 ms
这种行为有原因吗?
x86_64-pc-linux-gnu 上的 PostgreSQL 12.3,由 gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5) 编译,64 位
这有一个非常简单(也不是很好)的原因。 规划器假设 id_transformace = 1293 的行均匀分布在整个表中,因此它将能够通过 seq 扫描非常快速地收集其中的 100 个,然后提前停止。 但是这个假设是非常错误的,需要go 通过表的一大块找到100 个符合条件的行。
这个假设不是基于在表上收集的任何统计信息,因此增加统计信息目标将无济于事。 扩展统计信息也无济于事,因为它只提供列之间的统计信息,而不是列和物理排序之间的统计信息。
纯粹在股票服务器端没有很好的干净方法来解决这个问题。 一种解决方法是在运行查询之前set enable_seqscan=off
,然后重置后记。 另一种方法是将ORDER BY random()
添加到您的查询中,这样计划者就知道它不能提前停止。 或者扩展pg_hint_plan可能会有所帮助,我从未使用过它。
您可能会通过调整您的一些 *_cost 参数来更改计划,但这可能会使其他事情变得更糟。 看到使用 enable_seqscan=off 运行的 LIMITed 查询的 EXPLAIN (ANALYZE, BUFFERS) 的 output 可以告知该决定。
由于该列似乎稀疏/倾斜,您可以尝试增加统计信息大小:
ALTER TABLE testj.cdc_s5_gpps_ind
ALTER COLUMN id_transformace SET STATISTICS 1000;
ANALYZE testj.cdc_s5_gpps_ind;
Postgres-11 及更高版本还具有扩展的统计信息,允许识别和利用多列相关性。 您必须对表中数据的实际结构有所了解才能有效地使用它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.