为什么 Postgres 不为我的查询使用更好的索引?
Why does Postgres not use better index for my query?
我有一个 table 记录谁在类似 Twitter 的应用程序上关注了谁:
\d follow
Table "public.follow" .
Column | Type | Modifiers
---------+--------------------------+-----------------------------------------------------
xid | text |
followee | integer |
follower | integer |
id | integer | not null default nextval('follow_id_seq'::regclass)
createdAt | timestamp with time zone |
updatedAt | timestamp with time zone |
source | text |
Indexes:
"follow_pkey" PRIMARY KEY, btree (id)
"follow_uniq_users" UNIQUE CONSTRAINT, btree (follower, followee)
"follow_createdat_idx" btree ("createdAt")
"follow_followee_idx" btree (followee)
"follow_follower_idx" btree (follower)
table 中的条目数超过一百万,当我 运行 解释查询分析时,我得到了这个:
explain analyze SELECT "follow"."follower"
FROM "public"."follow" AS "follow"
WHERE "follow"."followee" = 6
ORDER BY "follow"."createdAt" DESC
LIMIT 15 OFFSET 0;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.43..353.69 rows=15 width=12) (actual time=5.456..21.497
rows=15 loops=1)
-> Index Scan Backward using follow_createdat_idx on follow (cost=0.43..61585.45 rows=2615 width=12) (actual time=5.455..21.488 rows=15 loops=1)
Filter: (followee = 6)
Rows Removed by Filter: 62368
Planning time: 0.068 ms
Execution time: 21.516 ms
为什么它在 follow_createdat_idx
上执行反向索引扫描,如果它使用 follow_followee_idx
,执行速度可能会更快。
当第一次 运行ning 时,这个查询大约需要 33 毫秒,然后后续调用大约需要 22 毫秒,我觉得这个时间比较长。
我正在使用 Amazon RDS 提供的 Postgres 9.5。知道这里可能发生什么问题吗?
(follower, "createdAt")
上的多列索引 非常适合查询 - 正如您在测试中发现的那样。
由于"createdAt"
可以为NULL(未定义NOT NULL
),可能要加上NULLS LAST
来查询和索引:
...
ORDER BY "follow"."createdAt" DESC NULLS LAST
并且:
"follow_follower_createdat_idx" btree (follower, "createdAt" DESC NULLS LAST)
更多:
- PostgreSQL sort by datetime asc, null first?
还有 次要 其他性能影响:
(follower, "createdAt")
上的多列索引每行比 (follower)
上的简单索引大 8 字节 - 44 字节 vs 36。更多(btree 索引大多是同一页布局为表格):
- Making sense of Postgres row sizes
索引中涉及的列无法通过热更新进行更改。向索引添加更多列 可能 阻止此优化 - 鉴于列名,这似乎特别不可能。而且由于您在 ("createdAt")
上还有另一个索引,所以这无论如何都不是问题。更多:
在 ("createdAt")
上有另一个索引没有任何缺点(除了每个索引的维护成本(针对写入性能,而不是读取性能)。两个索引都支持不同的查询。您可能 需要 索引 ("createdAt")
也可能不需要。详细说明:
我有一个 table 记录谁在类似 Twitter 的应用程序上关注了谁:
\d follow
Table "public.follow" .
Column | Type | Modifiers
---------+--------------------------+-----------------------------------------------------
xid | text |
followee | integer |
follower | integer |
id | integer | not null default nextval('follow_id_seq'::regclass)
createdAt | timestamp with time zone |
updatedAt | timestamp with time zone |
source | text |
Indexes:
"follow_pkey" PRIMARY KEY, btree (id)
"follow_uniq_users" UNIQUE CONSTRAINT, btree (follower, followee)
"follow_createdat_idx" btree ("createdAt")
"follow_followee_idx" btree (followee)
"follow_follower_idx" btree (follower)
table 中的条目数超过一百万,当我 运行 解释查询分析时,我得到了这个:
explain analyze SELECT "follow"."follower"
FROM "public"."follow" AS "follow"
WHERE "follow"."followee" = 6
ORDER BY "follow"."createdAt" DESC
LIMIT 15 OFFSET 0;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.43..353.69 rows=15 width=12) (actual time=5.456..21.497
rows=15 loops=1)
-> Index Scan Backward using follow_createdat_idx on follow (cost=0.43..61585.45 rows=2615 width=12) (actual time=5.455..21.488 rows=15 loops=1)
Filter: (followee = 6)
Rows Removed by Filter: 62368
Planning time: 0.068 ms
Execution time: 21.516 ms
为什么它在 follow_createdat_idx
上执行反向索引扫描,如果它使用 follow_followee_idx
,执行速度可能会更快。
当第一次 运行ning 时,这个查询大约需要 33 毫秒,然后后续调用大约需要 22 毫秒,我觉得这个时间比较长。
我正在使用 Amazon RDS 提供的 Postgres 9.5。知道这里可能发生什么问题吗?
(follower, "createdAt")
上的多列索引
由于"createdAt"
可以为NULL(未定义NOT NULL
),可能要加上NULLS LAST
来查询和索引:
...
ORDER BY "follow"."createdAt" DESC NULLS LAST
并且:
"follow_follower_createdat_idx" btree (follower, "createdAt" DESC NULLS LAST)
更多:
- PostgreSQL sort by datetime asc, null first?
还有 次要 其他性能影响:
(follower, "createdAt")
上的多列索引每行比(follower)
上的简单索引大 8 字节 - 44 字节 vs 36。更多(btree 索引大多是同一页布局为表格):- Making sense of Postgres row sizes
索引中涉及的列无法通过热更新进行更改。向索引添加更多列 可能 阻止此优化 - 鉴于列名,这似乎特别不可能。而且由于您在
("createdAt")
上还有另一个索引,所以这无论如何都不是问题。更多:在
("createdAt")
上有另一个索引没有任何缺点(除了每个索引的维护成本(针对写入性能,而不是读取性能)。两个索引都支持不同的查询。您可能 需要 索引("createdAt")
也可能不需要。详细说明: