Postgres 在查询计划中使用了错误的索引

Postgres uses wrong index in query plan

下面我有2个几乎相同的查询,只是限制不同。 然而,查询计划和执行时间完全不同。第一个查询比第二个查询慢 +300 倍。

只有少数 owner_ids 会出现此问题。所有者拥有许多路线 (+1000),其中 none 最近被编辑。 table 路由包含 2,806,976 行。示例中的所有者有 4,510 条路线。

数据库托管在具有 34.2 GiB 内存、4vCPU 和预配置 IOPS(实例类型 db.m2.2xlarge)的服务器上的 Amazon RDS 上。

EXPLAIN ANALYZE SELECT
    id
FROM
    route
WHERE
    owner_id = 39127
ORDER BY
    edited_date DESC
LIMIT
    5

Query plan:
"Limit  (cost=0.43..5648.85 rows=5 width=12) (actual time=1.046..12949.436 rows=5 loops=1)"
"  ->  Index Scan Backward using route_i_edited_date on route  (cost=0.43..5368257.28 rows=4752 width=12) (actual time=1.042..12949.418 rows=5 loops=1)"
"        Filter: (owner_id = 39127)"
"        Rows Removed by Filter: 2351712"
"Total runtime: 12949.483 ms"

EXPLAIN ANALYZE SELECT
    id
FROM
    route
WHERE
    owner_id = 39127
ORDER BY
    edited_date DESC
LIMIT
    15

Query plan:
"Limit  (cost=13198.79..13198.83 rows=15 width=12) (actual time=37.781..37.821 rows=15 loops=1)"
"  ->  Sort  (cost=13198.79..13210.67 rows=4752 width=12) (actual time=37.778..37.790 rows=15 loops=1)"
"        Sort Key: edited_date"
"        Sort Method: top-N heapsort  Memory: 25kB"
"        ->  Index Scan using route_i_owner_id on route  (cost=0.43..13082.20 rows=4752 width=12) (actual time=0.039..32.425 rows=4510 loops=1)"
"              Index Cond: (owner_id = 39127)"
"Total runtime: 37.870 ms"

如何确保 Postgres 使用索引 route_i_owner_id.

我已经尝试过以下方法:

使用以下复合索引解决:

CREATE INDEX route_i_owner_id_edited_date
  ON public.route
  USING btree
  (owner_id, edited_date DESC);

EXPLAIN ANALYZE SELECT
    id
FROM
    route
WHERE
    owner_id = 39127
ORDER BY
    edited_date DESC
LIMIT
    5

"Limit  (cost=0.43..16.99 rows=5 width=12) (actual time=0.028..0.050 rows=5 loops=1)"
"  ->  Index Scan using route_i_owner_id_edited_date on route  (cost=0.43..15746.74 rows=4753 width=12) (actual time=0.025..0.039 rows=5 loops=1)"
"        Index Cond: (owner_id = 39127)"
"Total runtime: 0.086 ms"

这个查询一开始就很慢。应该不到 1 秒。

您的第一个示例使用 edited_date 索引首先对数据进行排序,然后过滤排序后的数据。

您的第二个示例对数据进行排序(似乎没有索引),然后应用索引扫描来获取实际行。这两种方法似乎都不好。

可能会加快速度的是 owner_id 和 edited_date 的复合索引,如果经常使用这种查询,这将很有意义。该索引还将替换其他索引之一,甚至可能同时替换两者。