sql - 在 postgres 中使用 LIMIT 时不使用索引

标签 sql postgresql indexing

我有一个单词表,索引在 (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/

相关文章:

jquery - 使用 jQuery 从给定索引的选择器中获取元素

R:使用 get() 和 paste() 返回列

MySQL INSERT INTO 单列从 SELECT 查询中从另外两个表中提取数据 - 抛出 #1062 - 键 2 的重复条目 '' 错误

PHP MySQL SELECT 文本被截断

sql - MySQL 查询 - 可以包含这个子句吗?

sql - 在限定时间内获得 COUNT() 结果

python - 如果 postgres 连接刷新,我是否需要重新创建临时表?

MySql 没有为少数查询选择正确的索引

c++ - libpq++ 或 libpqxx 是官方的 c++ postgres 接口(interface)吗?

c# - RavenDB Map Reduce 非重复索引