Postgres 在几乎每个查询中都使用 primary_key 索引

Posted

技术标签:

【中文标题】Postgres 在几乎每个查询中都使用 primary_key 索引【英文标题】:Postgres using primary_key index in almost every query 【发布时间】:2018-01-13 06:40:50 【问题描述】:

我们正在将我们的 postgres 数据库从版本 9.3.14 升级到 9.4.9。我们目前处于测试阶段。我们在测试时遇到了一个问题,当数据库更新到 9.4.9 时会导致 CPU 使用率过高。有些查询 Postgres 9.4 正在使用 primary_key_index 而有更便宜的选项。例如,为以下查询运行解释分析:

SELECT  a.id as a_id, b.col_id as col_id
FROM a
INNER JOIN b ON b.id = a.b_id
WHERE (a.col_text = 'pqrs' AND a.col_int = 1)
ORDER BY a.id ASC LIMIT 1

给出这个:

Limit  (cost=0.87..4181.94 rows=1 width=8) (actual time=93014.991..93014.992 rows=1 loops=1)
 ->  Nested Loop  (cost=0.87..1551177.78 rows=371 width=8) (actual time=93014.990..93014.990 rows=1 loops=1)
       ->  Index Scan using a_pkey on a  (cost=0.43..1548042.20 rows=371 width=8) (actual time=93014.968..93014.968 rows=1 loops=1)
             Filter: ((col_int = 1) AND ((col_text)::text = 'pqrs'::text))
             Rows Removed by Filter: 16114217
       ->  Index Scan using b_pkey on b  (cost=0.43..8.44 rows=1 width=8) (actual time=0.014..0.014 rows=1 loops=1)
             Index Cond: (id = a.b_id)
Planning time: 0.291 ms
Execution time: 93015.041 ms

虽然 9.3.14 中相同查询的查询计划给出了这样的:

Limit  (cost=17.06..17.06 rows=1 width=8) (actual time=5.066..5.067 rows=1 loops=1)
 ->  Sort  (cost=17.06..17.06 rows=1 width=8) (actual time=5.065..5.065 rows=1 loops=1)
       Sort Key: a.id
       Sort Method: quicksort  Memory: 25kB
       ->  Nested Loop  (cost=1.00..17.05 rows=1 width=8) (actual time=5.047..5.049 rows=1 loops=1)
             ->  Index Scan using index_a_on_col_text on a  (cost=0.56..8.58 rows=1 width=8) (actual time=3.154..3.155 rows=1 loops=1)
                   Index Cond: ((col_text)::text = 'pqrs'::text)
                   Filter: (col_int = 1)
             ->  Index Scan using b_pkey on b  (cost=0.43..8.46 rows=1 width=8) (actual time=1.888..1.889 rows=1 loops=1)
                   Index Cond: (id = a.b_id)
Total runtime: 5.112 ms

如果我从查询中删除 ORDER BY 子句,则查询使用正确的索引可以正常工作。我可以理解,在这种情况下(使用 ORDER BY),规划器正在尝试使用主键索引来扫描所有行并获取有效行。但很明显,使用 sort 显式便宜得多。

我探索了 Postgres 参数,例如 enable_indexscanenable_seqscan,它们默认是 on。我们希望将其留在数据库中以决定进行索引扫描或顺序扫描。我们还尝试调整 effective_cache_sizerandom_page_costseq_page_costenable_sort 也开启了。

这不仅发生在这个特定的查询中,而且还有一些其他查询正在使用 primary_key_index 而不是其他可能的有效方法。

PS:

【问题讨论】:

愚蠢的问题:你有没有运行VACUUM ANALYZE来更新统计数据? 是的。试过了,但没有结果。 【参考方案1】:

向 AWS Support 提交案例后,我得到了以下信息:

我了解到您想知道性能下降的原因 在您最近升级的实例上。这是预期的和一般的 Postgres 实例上的升级行为。升级完成后, 您需要在每个用户数据库上运行 ANALYZE 以更新统计信息 桌子。这也使 SQL 性能更好。更好的方法 即使用vacuumdb[1],像这样:

vacuumdb -U [你的用户] -d [你的数据库] -Ze -h [你的 rds 端点]

它只会优化您的数据库执行计划,不会释放空间, 但比完全吸尘花费的时间更少。

这已经解决了这个问题。希望这可以帮助其他偶然发现此类问题的人。

【讨论】:

以上是关于Postgres 在几乎每个查询中都使用 primary_key 索引的主要内容,如果未能解决你的问题,请参考以下文章

几乎在每个url中都用-in替换uu(加上movabletype中的漂亮标记url)

如何在子查询中使用 select 语句? (Postgres)

Postgres 中的时间序列查询

在 Postgres 中,如何平均每个用户的最新 5 个分数?

复杂的 Postgres 查询问题 [关闭]

Postgres 9.6 如何遍历数组并将每个数组值插入表中?