极慢 "row_number() over order" 查询

Extremely slow "row_number() over order" query

我有一个用户 table 有列(id、xp、...)和大约 150 万行。

我通过以下查询(执行时间为 33 秒)获得某人在 XP 排行榜中的位置:

EXPLAIN ANALYZE WITH counts AS (
                SELECT DISTINCT
                                id,
                                ROW_NUMBER () OVER (ORDER BY xp DESC)
                FROM
                    users
            ) SELECT
                *
            FROM
                counts
            WHERE
                id=1;
Subquery Scan on counts  (cost=344492.80..395160.57 rows=7404 width=16) (actual time=30683.244..32174.117 rows=1 loops=1)
  Filter: (counts.id = '1'::bigint)
  Rows Removed by Filter: 1481060
  ->  HashAggregate  (cost=344492.80..376651.79 rows=1480702 width=24) (actual time=30679.440..32034.921 rows=1481061 loops=1)
        Group Key: users.id, row_number() OVER (?)"
        Planned Partitions: 64  Batches: 65  Memory Usage: 4369kB  Disk Usage: 125960kB
        ->  WindowAgg  (cost=212155.06..238067.34 rows=1480702 width=24) (actual time=2983.137..20302.548 rows=1481061 loops=1)
              ->  Sort  (cost=212155.06..215856.81 rows=1480702 width=16) (actual time=2983.082..5040.782 rows=1481061 loops=1)
                    Sort Key: users.xp DESC
                    Sort Method: external merge  Disk: 37760kB
                    ->  Seq Scan on users  (cost=0.00..35094.02 rows=1480702 width=16) (actual time=25.467..880.626 rows=1481061 loops=1)
Planning Time: 2.593 ms
JIT:
  Functions: 14
  Options: Inlining false, Optimization false, Expressions true, Deforming true"
  Timing: Generation 12.061 ms, Inlining 0.000 ms, Optimization 1.503 ms, Emission 26.086 ms, Total 39.650 ms"
Execution Time: 32325.206 ms

我的table定义:

CREATE TABLE users
(
    id                    bigint                                        NOT NULL
        CONSTRAINT users_pkey
            PRIMARY KEY,
    xp                    bigint               DEFAULT 0                NOT NULL,
    ...
);
CREATE INDEX user_xp_leaderboard_index
    ON users (xp DESC, id ASC);

但是速度极慢。虽然考虑到它对整个 table 进行排序并对其进行过滤并不奇怪,但我不知道如何 improve/optimize 这个查询。

我做到了SET work_mem TO '1 GB';。有点帮助,但帮助不大。

如有任何帮助,我们将不胜感激。提前致谢。

您可以这样写查询:

select count(*)
from users u 
where u.xp >= (select u2.xp from users u2 where u2.id = 1);

这可以利用 users(id, xp) 上的索引。这应该完全消除任何排序。如果行很宽并且 Postgres 可以使用仅索引扫描,users(xp) 上的索引也可能有帮助。