我有一个单词表,索引在 (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)
为什么会这样?如何获得在所有情况下都使用的索引?
谢谢。
最佳答案
查看有关 Using EXPLAIN 的 PostgreSQL 文档和 Query Planning .在 LIMIT 100
情况下,查询规划器更喜欢顺序扫描而不是索引扫描的原因很简单,因为顺序扫描成本更低。
查询中没有 ORDER BY
子句,因此规划器可以处理与过滤条件匹配的前 100(随机)行。索引扫描需要先读取索引页,然后读取数据页以获取相应的行。顺序扫描只需要读取数据页来获取行。在您的情况下,表统计信息似乎表明有足够的(随机)行符合过滤条件。顺序页面读取以获得 100 行的成本被认为比先读取索引然后获取实际行的成本更低。当您提高限制或匹配过滤条件的行数减少时,您可能会看到不同的计划。
在默认设置下,规划器认为随机页面读取的成本 (random_page_cost) 是顺序页面读取的成本 (seq_page_cost) 的四倍。可以调整这些设置以调整查询计划(例如,当整个数据库在 RAM 中时,随机页面读取并不比顺序页面读取更昂贵,索引扫描应该是首选)。您还可以通过启用/禁用某些类型的扫描来尝试不同的查询计划,例如:
set enable_seqscan = [on | off]
set enable_indexscan = [on | off]
虽然可以在全局范围内启用/禁用某些类型的扫描,但这应该仅用于在每个 session 的基础上进行临时调试或故障排除。
在测试查询计划之前还要运行 VACUUM ANALYZE words
,否则在测试之间运行的自动清理 (autovaccum) 可能会影响结果。
关于sql - 在 postgres 中使用 LIMIT 时不使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8566931/