database - 提高 PostgreSQL 中的 OFFSET 性能

标签 database postgresql query-optimization

我有一个表,我在 LIMIT 和 OFFSET 之前执行 ORDER BY 以进行分页。

在 ORDER BY 列上添加索引会对性能产生巨大影响(当与较小的 LIMIT 结合使用时)。在一个 500,000 行的表上,我看到添加索引后性能提高了 10,000 倍,只要有一个小的 LIMIT。

但是,该索引对高 OFFSET 没有影响(即我分页中的后面的页面)。这是可以理解的:b 树索引可以很容易地从头开始按顺序迭代,但找不到第 n 项。

似乎计数的 b 树索引 会有所帮助,但我不知道 PostgreSQL 是否支持这些索引。还有其他解决方案吗?似乎针对大型 OFFSET 进行优化(尤其是在分页用例中)并不罕见。

不幸的是,PostgreSQL 手册只是说“被 OFFSET 子句跳过的行仍然需要在服务器内部计算;因此大的 OFFSET 可能效率低下。”

最佳答案

您可能需要计算索引。

让我们创建一个表:

create table sales(day date, amount real);

并用一些随机的东西填充它:

insert into sales 
    select current_date + s.a as day, random()*100 as amount
    from generate_series(1,20);

按天索引,这里没什么特别的:

create index sales_by_day on sales(day);

创建一个行位置函数。还有其他方法,这是最简单的方法:

create or replace function sales_pos (date) returns bigint 
   as 'select count(day) from sales where day <= $1;' 
   language sql immutable;

检查它是否有效(尽管不要在大型​​数据集上这样调用它):

select sales_pos(day), day, amount from sales;

     sales_pos |    day     |  amount  
    -----------+------------+----------
             1 | 2011-07-08 |  41.6135
             2 | 2011-07-09 |  19.0663
             3 | 2011-07-10 |  12.3715
    ..................

现在是棘手的部分:添加另一个根据 sales_pos 函数值计算的索引:

create index sales_by_pos on sales using btree(sales_pos(day));

这里是你如何使用它。 5 是你的“偏移量”,10 是“限制”:

select * from sales where sales_pos(day) >= 5 and sales_pos(day) < 5+10;

        day     | amount  
    ------------+---------
     2011-07-12 | 94.3042
     2011-07-13 | 12.9532
     2011-07-14 | 74.7261
    ...............

它很快,因为当你这样调用它时,Postgres 使用索引中的预先计算值:

explain select * from sales 
  where sales_pos(day) >= 5 and sales_pos(day) < 5+10;

                                    QUERY PLAN                                
    --------------------------------------------------------------------------
     Index Scan using sales_by_pos on sales  (cost=0.50..8.77 rows=1 width=8)
       Index Cond: ((sales_pos(day) >= 5) AND (sales_pos(day) < 15))

希望对您有所帮助。

关于database - 提高 PostgreSQL 中的 OFFSET 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6618366/

相关文章:

android - 如何将用户输入的数据保存到编辑文本中,以便在同一应用程序的另一个 Activity 中查看

sql - 通过 Ant 构建文件创建数据库(而不是填充它)

mysql - 如何加速这个 MySQL 查询?

php - 从数据库中提取数据并放入 html 表中

database - Hibernate,将多个列分配为主键

postgresql - 如何允许在本地连接上没有密码提示的情况下登录 postgres?

sql - 在 postgres 中将行聚合为 jsonb

php - 如何为 laravel 设置默认的 postgresql 架构?

sql - 优化一个奇怪的 MySQL 查询

针对最大值的 MySQL 查询优化