database - 为什么 Postgres 不为我的查询使用更好的索引?

标签 database postgresql indexing sql-order-by postgresql-performance

我有一个表格,用于记录谁在类似 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)

表中的条目数超过一百万,当我对查询运行解释分析时,我得到了这个:

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 执行速度会更快。

这个查询在第一次运行时大约需要 33 毫秒,然后后续调用大约需要 22 毫秒,我觉得这个时间比较长。

我正在使用 Amazon RDS 提供的 Postgres 9.5。知道这里可能发生什么错误吗?

最佳答案

(follower, "createdAt") 上的多列索引 user1937198 suggested非常适合查询 - 正如您已经在测试中发现的那样。

由于 "createdAt" 可以为 NULL(未定义 NOT NULL),您可能需要添加 NULLS LAST 查询索引:

...
ORDER BY "follow"."createdAt" DESC NULLS LAST

和:

"follow_follower_createdat_idx" btree (follower, "createdAt" DESC NULLS LAST)

更多:

还有次要其他性能影响:

  • (follower, "createdAt") 上的多列索引每行比 (follower) 上的简单索引大 8 字节 - 44 字节对 36 字节. 更多(btree 索引与表的页面布局基本相同):

  • 以任何方式参与索引的列都不能通过热更新进行更改。向索引添加更多列可能 会阻止此优化 - 考虑到列名,这似乎特别不可能。并且由于您在 ("createdAt") 上还有另一个索引,所以这无论如何都不是问题。更多:

  • ("createdAt") 上设置另一个索引没有任何缺点(除了每个索引的维护成本(写入性能,而不是读取性能)。两个索引都支持不同的查询。您可能需要另外在 ("createdAt") 上添加索引。详细说明:

关于database - 为什么 Postgres 不为我的查询使用更好的索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43833559/

相关文章:

django - 有没有一种简单的方法可以将 Django 的模型和迁移链与数据库验证一致性进行比较?

postgresql - 使用 Docker 进行集成测试后恢复数据库状态?

java - jooq - 如何在 jooq 中将数组绑定(bind)为参数 - PostgreSQL

c++ - C++的多重关联问题

c# - 以编程方式阻止 Vista 桌面搜索 (WORDS) 索引放置在映射网络驱动器上的 pst 文件

sql-server-2005 - 查明索引和表统计信息是否过时

java - 使用 SessionFactory 时 Hibernate 中的奇怪 NumberFormatException

C# 枚举和数据库表

postgresql - java - PSQLException : ERROR: syntax error at or near "$1"

PHP:文件还是数据库?