我有桌子
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甚至更新版本都有一些功能?我搜索过但没找到任何东西。
答案 0 :(得分:21)
大OFFSET
总是很慢。 Postgres必须订购所有行并将可见计算到您的偏移量。要直接跳过 之前的所有行,您可以在表格中添加一个已编入索引的row_number
(或创建包含所述row_number
的{{3}}并使用WHERE row_number > x
代替OFFSET x
。
但是,这种方法仅适用于只读(或大部分)数据。对可以同时更改并发的表数据实现相同的操作更具挑战性。您需要首先定义所需的行为完全。
我建议使用分页的不同方法:
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
vote_x
和id_x
来自 上一页 的 last 行(适用于{{}} 1}}和DESC
)。或者从第一个开始向后。
您已经拥有的索引支持比较行值 - 这是一个符合ANSI SQL的功能,但不是每个RDBMS都支持它。
ASC
或者按降序排列:
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
中的混合方向,如:
ORDER BY
至少我不能想到一种泛型方式来有效地实现它。如果两列中至少有一列是数字类型,则可以使用ORDER BY vote ASC, id DESC
上具有反转值的功能索引 - 并在(vote, (id * -1))
中使用相同的表达式:
ORDER BY
相关:
特别注意Markus Winand的演讲我链接到:
答案 1 :(得分:-4)
你有没有尝试过分桌?
易于管理,提高可扩展性和可用性,以及a 减少阻塞是分区表的常见原因。 提高查询性能不是采用分区的理由, 虽然在某些情况下它可能是有益的副作用。就......而言 性能,重要的是要确保您的实施计划 包括查询性能的审查。确认您的索引 在表格之后继续适当地支持您的查询 分区,并使用clustered和。验证查询 非聚集索引受益于分区消除 适用。
http://sqlperformance.com/2013/09/sql-indexes/partitioning-benefits