我有桌子
create table big_table (
id serial primary key,
-- other columns here
vote int
);
这个表很大,大约有7000万行,我需要查询:
SELECT * FROM big_table
ORDER BY vote [ASC|DESC], id [ASC|DESC]
OFFSET x LIMIT n -- I need this for pagination
你可能知道,当
x
很大时,这样的查询非常慢。
为了性能优化,我添加了索引:
create index vote_order_asc on big_table (vote asc, id asc);
和
create index vote_order_desc on big_table (vote desc, id desc);
EXPLAIN
显示上面的SELECT
查询使用了这些索引,但是偏移量很大,无论如何它都非常慢。
如何在大表中优化使用
OFFSET
的查询?也许 PostgreSQL 9.5 甚至更新版本有一些功能?我已经搜索过但没有找到任何东西。
大的
OFFSET
总是会很慢。 Postgres 必须对所有行进行排序,并将“可见”行计数到您的偏移量。要直接跳过所有前面的行 ,您可以将索引 row_number
添加到表中(或创建一个 MATERIALIZED VIEW
包括所述
row_number
)并使用 WHERE row_number > x
而不是 OFFSET x
。但是,这种方法仅适用于只读(或大部分)数据。对可以“同时”更改的表数据实施相同的方法更具挑战性。您需要首先准确地定义所需的行为。 我建议采用不同的方法: “键集分页”的行值比较
参见:
'WHERE (col1, col2) 的 SQL 语法术语
vote_x
和
id_x
来自
上一页的第最后行(对于DESC
和ASC
)。或者从first(如果导航
向后)。
(vote, id)
需要是 UNIQUE
才能获得确定性结果。
您已有的索引支持比较行值 - 一个标准 SQL 功能,但并非每个 RDBMS 都支持它。CREATE INDEX vote_order_asc ON big_table (vote, id);
或者降序排列:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
可以使用相同的索引。
我建议您声明您的列NOT NULL
或熟悉 NULLS FIRST|LAST
结构:
ROW
WHERE
值不能用单独的成员字段替换。
WHERE (vote, id) > (vote_x, id_x)
不能
WHERE vote >= vote_x
AND id > id_x
这将排除带有 id <= id_x
的所有行,而我们只想针对同一次投票而不是下一次投票这样做。正确的等价物是:
WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
单列来说,
会很简单。这就是我一开始提到的特殊情况。
注2 该技术
不适用于ORDER BY
,例如:
ORDER BY vote ASC, id DESC
(vote, (id * -1))
上使用具有倒排值的函数索引 - 并在
ORDER BY
中使用相同的表达式:
ORDER BY vote ASC, (id * -1) ASC
相关:
提高多个表中列的排序性能
“以 PostgreSQL 方式完成分页”