我有一个带有索引的单词表(language_id,state)。以下是EXPLAIN ANALYZE的结果:
无限制
explain analyze SELECT "words".* FROM "words" WHERE (words.language_id = 27) AND (state IS NULL);
Bitmap Heap Scan on words (cost=10800.38..134324.10 rows=441257 width=96) (actual time=233.257..416.026 rows=540556 loops=1)
Recheck Cond: ((language_id = 27) AND (state IS NULL))
-> Bitmap Index Scan on ls (cost=0.00..10690.07 rows=441257 width=0) (actual time=230.849..230.849 rows=540556 loops=1)
Index Cond: ((language_id = 27) AND (state IS NULL))
Total runtime: 460.277 ms
(5 rows)
限制100
explain analyze SELECT "words".* FROM "words" WHERE (words.language_id = 27) AND (state IS NULL) LIMIT 100;
Limit (cost=0.00..51.66 rows=100 width=96) (actual time=0.081..0.184 rows=100 loops=1)
-> Seq Scan on words (cost=0.00..227935.59 rows=441257 width=96) (actual time=0.080..0.160 rows=100 loops=1)
Filter: ((state IS NULL) AND (language_id = 27))
Total runtime: 0.240 ms
(4 rows)
为什么会这样?如何在所有情况下都使用索引?
感谢。
答案 0 :(得分:7)
我认为PostgreSQL查询规划器只是认为在第二种情况下 - 具有LIMIT的情况 - 因为它[LIMIT]太小而不值得应用索引。所以这不是问题。
答案 1 :(得分:5)
查看有关Using EXPLAIN和Query Planning的PostgreSQL文档。查询计划程序更喜欢在LIMIT 100
情况下对索引扫描进行顺序扫描的原因仅仅是因为顺序扫描更便宜。
查询中没有ORDER BY
子句,因此规划器可以使用与过滤条件匹配的前100个(随机)行。索引扫描需要首先读取索引页,然后读取数据页以获取相应的行。顺序扫描只需读取数据页面即可获取行。在您的情况下,表统计信息似乎表明有足够的(随机)行匹配过滤条件。获取100行的顺序页面读取的成本被认为比首先读取索引的成本便宜,然后获取实际的行。当您提高限制或较少的行与过滤条件匹配时,您可能会看到不同的计划。
使用默认设置,计划程序会将随机页面读取的成本( random_page_cost )视为连续页面读取成本的四倍( seq_page_cost )。可以调整这些设置以调整查询计划(例如,当整个数据库在RAM中时,随机页面读取并不比顺序页面读取更昂贵并且应该优选索引扫描)。您还可以通过启用/禁用某些类型的扫描来尝试不同的查询计划,例如:
set enable_seqscan = [on | off]
set enable_indexscan = [on | off]
虽然可以在全局范围内启用/禁用某些类型的扫描,但这应该仅用于每个会话的调试或故障排除。
在测试查询计划之前运行VACUUM ANALYZE words
,否则测试之间运行的自动真空( autovaccum )可能会影响结果。
答案 2 :(得分:3)
无限制:rows = 540556 loops = 1总运行时间:460.277 ms
使用limit:rows = 100 loops = 1 Total runtime:0.240 ms
我在这里看不到问题。如果您的查询产生500K行,则需要更多时间。
答案 3 :(得分:0)
两个查询返回不同数量的行也很奇怪。我猜你一直在插入...嗯,如果你做一个子选择怎么办?
select * from (select ...) limit 100;