pg.ddx.io  pgsql-performance@postgresql.org mailing list archive  
help / color / mirror / Atom feed
Poor OFFSET performance in PostgreSQL 9.1.6
6+ messages / 5 participants
[nested] [flat]

* Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-28 20:39  fburgess@radiantblue.com
  0 siblings, 4 replies; 6+ messages in thread

From: fburgess@radiantblue.com @ 2013-08-28 20:39 UTC (permalink / raw)
  To: pgsql-performance


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance

Attachments:

tuning_SQL_with_offest.txt (text/plain, 4.0K) download The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient. Simple Query cached resuits Select * from labor_task_report this_ inner join labor_tasks labor1_ on this_.labor_UID=20178 order by labor1_START_TIME asc, this_.work_DATE_TIME asc, this_.work_UID asc, this_.task_REPORT_UID limit 10000 offset 940000; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- Limit (cost=193432.05..193432.05 rows=1 width=1158) (actual time=34369.092..34376 rows=10000 loops=1) -> Sort (cost=193256.16..193432.05 rows=70354 width=1158) (actual time=33764.533..34322.856 rows=940000 loops=1) Sort key: labor1_.start_time, this_.work_date_time, this_.work_uid, this_.task_report_uid Sort Method: external sort Disk: 1112224kB -> Nested Loop (cost=0.00..162169.34 rows=70354 width=1158) (actual time=175.388..3642.170 rows=2410585 loops=1) -> Index Scan using corporate_labor_pkey on labor_tasks labor1_ (cost=0.00..4.27 rows=1 width=954) (actual time=0.017..0.020 rows=1 loops=1) Index Cond: (labor_uid = 20178) -> Append (cost=0.00..161461.53 rows=70354 width=204) (actual time=175.362..2769.808 rows=2410585 loops=1) -> Seq Scan on labor_task_report this_ (cost=0.00..0.00 rows=1 width=648) (actual time=0.001..0.001 rows=0 loops=1) FIlter: (labor_uid = 20178) -> Index Scan using idx_task_report_by_labor_uid_y2008m01 on corporate_task_report_y2008m01 this_ (cost=0.00..19.97 rows=1 width=204) (actual time=0.227..1.227 rows=0 loops=1) Index Cond: (labor_uid = 20178) ... ... ... -> Index Scan using idx_task_report_by_labor_uid_y2013m12 on corporate_task_report_y2013m12 this_ (cost=0.00..8.28 rows=1 width=204) (actual time=0.056..1.056 rows=0 loops=1) Index Cond: (labor_uid = 20178) Total runtime: 34890.400 ms <- 34 seconds Very same query, but without offset QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- Limit (cost=167195.33..167220.33 rows=10000 width=1158) (actual time=6365.434..6367.545 rows=10000 loops=1) -> Sort (cost=167195.33..167371.21 rows=70354 width=1158) (actual time=6365.432..6366.413 rows=10000 loops=1) Sort key: labor1_.start_time, this_.work_date_time, this_.work_uid, this_.task_report_uid Sort Method: top-N heapsort Memory: 5925kB -> Nested Loop (cost=0.00..162169.34 rows=70354 width=1158) (actual time=176.395..2847.287 rows=2410585 loops=1) -> Index Scan using corporate_labor_pkey on labor_tasks labor1_ (cost=0.00..4.27 rows=1 width=954) (actual time=0.016..0.020 rows=1 loops=1) Index Cond: (labor_uid = 20178) -> Append (cost=0.00..161461.53 rows=70354 width=204) (actual time=176.370..2116.998 rows=2410585 loops=1) -> Seq Scan on labor_task_report this_ (cost=0.00..0.00 rows=1 width=648) (actual time=0.001..0.001 rows=0 loops=1) FIlter: (labor_uid = 20178) -> Index Scan using idx_task_report_by_labor_uid_y2008m01 on corporate_task_report_y2008m01 this_ (cost=0.00..19.97 rows=1 width=204) (actual time=0.227..1.227 rows=0 loops=1) Index Cond: (labor_uid = 20178) ... ... ... -> Index Scan using idx_task_report_by_labor_uid_y2013m12 on corporate_task_report_y2013m12 this_ (cost=0.00..8.28 rows=1 width=204) (actual time=0.056..1.056 rows=0 loops=1) Index Cond: (labor_uid = 20178) Total runtime: 6368.894 ms <- 6.3 seconds
^ permalink raw reply [nested|flat] 6+ messages in thread

* Re: Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-28 21:26  Greg Spiegelberg <gspiegelberg@gmail.com>
  parent: fburgess@radiantblue.com
  3 siblings, 0 replies; 6+ messages in thread

From: Greg Spiegelberg @ 2013-08-28 21:26 UTC (permalink / raw)
  To: fburgess@radiantblue.com; +Cc: pgsql-performance

Two solutions come to mind.  First possibility is table partitioning on the
column you're sorting.  Second, depending on your application, is to use a
cursor.  Cursor won't help with web applications however a stateful
application could benefit.

HTH
-Greg


On Wed, Aug 28, 2013 at 2:39 PM, <fburgess@radiantblue.com> wrote:

> Can anyone offer suggestions on how I can optimize a query that contains
> the LIMIT OFFSET clause?
>
> The explain plan of the query is included in the notepad attachment.
>
> thanks
>
>
>
>
> --
> Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-performance
>
>

^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-28 22:08  fburgess@radiantblue.com
  0 siblings, 0 replies; 6+ messages in thread

From: fburgess@radiantblue.com @ 2013-08-28 22:08 UTC (permalink / raw)
  To: Greg Spiegelberg <gspiegelberg@gmail.com>


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance

Attachments:

tuning_SQL_with_offest.txt (text/plain, 4.0K) download The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient. Simple Query cached resuits Select * from labor_task_report this_ inner join labor_tasks labor1_ on this_.labor_UID=20178 order by labor1_START_TIME asc, this_.work_DATE_TIME asc, this_.work_UID asc, this_.task_REPORT_UID limit 10000 offset 940000; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- Limit (cost=193432.05..193432.05 rows=1 width=1158) (actual time=34369.092..34376 rows=10000 loops=1) -> Sort (cost=193256.16..193432.05 rows=70354 width=1158) (actual time=33764.533..34322.856 rows=940000 loops=1) Sort key: labor1_.start_time, this_.work_date_time, this_.work_uid, this_.task_report_uid Sort Method: external sort Disk: 1112224kB -> Nested Loop (cost=0.00..162169.34 rows=70354 width=1158) (actual time=175.388..3642.170 rows=2410585 loops=1) -> Index Scan using corporate_labor_pkey on labor_tasks labor1_ (cost=0.00..4.27 rows=1 width=954) (actual time=0.017..0.020 rows=1 loops=1) Index Cond: (labor_uid = 20178) -> Append (cost=0.00..161461.53 rows=70354 width=204) (actual time=175.362..2769.808 rows=2410585 loops=1) -> Seq Scan on labor_task_report this_ (cost=0.00..0.00 rows=1 width=648) (actual time=0.001..0.001 rows=0 loops=1) FIlter: (labor_uid = 20178) -> Index Scan using idx_task_report_by_labor_uid_y2008m01 on corporate_task_report_y2008m01 this_ (cost=0.00..19.97 rows=1 width=204) (actual time=0.227..1.227 rows=0 loops=1) Index Cond: (labor_uid = 20178) ... ... ... -> Index Scan using idx_task_report_by_labor_uid_y2013m12 on corporate_task_report_y2013m12 this_ (cost=0.00..8.28 rows=1 width=204) (actual time=0.056..1.056 rows=0 loops=1) Index Cond: (labor_uid = 20178) Total runtime: 34890.400 ms <- 34 seconds Very same query, but without offset QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- Limit (cost=167195.33..167220.33 rows=10000 width=1158) (actual time=6365.434..6367.545 rows=10000 loops=1) -> Sort (cost=167195.33..167371.21 rows=70354 width=1158) (actual time=6365.432..6366.413 rows=10000 loops=1) Sort key: labor1_.start_time, this_.work_date_time, this_.work_uid, this_.task_report_uid Sort Method: top-N heapsort Memory: 5925kB -> Nested Loop (cost=0.00..162169.34 rows=70354 width=1158) (actual time=176.395..2847.287 rows=2410585 loops=1) -> Index Scan using corporate_labor_pkey on labor_tasks labor1_ (cost=0.00..4.27 rows=1 width=954) (actual time=0.016..0.020 rows=1 loops=1) Index Cond: (labor_uid = 20178) -> Append (cost=0.00..161461.53 rows=70354 width=204) (actual time=176.370..2116.998 rows=2410585 loops=1) -> Seq Scan on labor_task_report this_ (cost=0.00..0.00 rows=1 width=648) (actual time=0.001..0.001 rows=0 loops=1) FIlter: (labor_uid = 20178) -> Index Scan using idx_task_report_by_labor_uid_y2008m01 on corporate_task_report_y2008m01 this_ (cost=0.00..19.97 rows=1 width=204) (actual time=0.227..1.227 rows=0 loops=1) Index Cond: (labor_uid = 20178) ... ... ... -> Index Scan using idx_task_report_by_labor_uid_y2013m12 on corporate_task_report_y2013m12 this_ (cost=0.00..8.28 rows=1 width=204) (actual time=0.056..1.056 rows=0 loops=1) Index Cond: (labor_uid = 20178) Total runtime: 6368.894 ms <- 6.3 seconds
^ permalink raw reply [nested|flat] 6+ messages in thread

* Re: Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-28 23:10  Merlin Moncure <mmoncure@gmail.com>
  parent: fburgess@radiantblue.com
  3 siblings, 0 replies; 6+ messages in thread

From: Merlin Moncure @ 2013-08-28 23:10 UTC (permalink / raw)
  To: fburgess@radiantblue.com; +Cc: pgsql-performance

On Wed, Aug 28, 2013 at 3:39 PM,  <fburgess@radiantblue.com> wrote:
> Can anyone offer suggestions on how I can optimize a query that contains the
> LIMIT OFFSET clause?
>
> The explain plan of the query is included in the notepad attachment.

OFFSET is working as designed (that is, slowly).  Managing pagination
with OFFSET is essentially a hack and will not scale to even medium
sized tables.   You have some SQL alternatives.  One is cursors as
greg mentioned.   Another is client side pagination:

Select * from labor_task_report this_
inner join labor_tasks labor1_ on this_.labor_UID=20178
order by
  labor1_START_TIME asc,
  this_.work_DATE_TIME asc,
  this_.work_UID asc,
  this_.task_REPORT_UID
limit 10000 offset 940000;

could become

Select * from labor_task_report this_
inner join labor_tasks labor1_ on this_.labor_UID=20178
where (
  labor1_START_TIME,
  this_.work_DATE_TIME asc,
  this_.work_UID asc,
  this_.task_REPORT_UID) >
  ($1, $2, $3, $4)
order by
  labor1_START_TIME asc,
  this_.work_DATE_TIME asc,
  this_.work_UID asc,
  this_.task_REPORT_UID
limit 10000;


where $1-$4 are the corresponding fields of the last row you read from
the last fetch.

merlin


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance



^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-29 04:43  David Rowley <dgrowleyml@gmail.com>
  parent: fburgess@radiantblue.com
  3 siblings, 0 replies; 6+ messages in thread

From: David Rowley @ 2013-08-29 04:43 UTC (permalink / raw)
  To: fburgess@radiantblue.com; +Cc: pgsql-performance

On Thu, Aug 29, 2013 at 8:39 AM, <fburgess@radiantblue.com> wrote:

> Can anyone offer suggestions on how I can optimize a query that contains
> the LIMIT OFFSET clause?
>
> The explain plan of the query is included in the notepad attachment.
>
> thanks
>
>
Before I write anything, I should warn that it has been a while since I had
to read an explain analyze without also having a schema design to back it
up, so I may have this wrong.

Since the sort must be applied before the limit and the join must take
place before the sort, it's naturally pretty slow given the number of rows
being processed.

It's hard to tell how the tables are constrained by looking at the explain
analyze. It seems that labor_uid is the primary key for the labor_tasks
table... (going by the 1 record that is returned. If so, then it's a bit
confusing why you'd need to sort on labor1.start_time at all, since only 1
row exists anyway...

I got that from here:

Index Scan using corporate_labor_pkey on labor_tasks labor1_
(cost=0.00..4.27 rows=1 width=954) (actual time=0.017..0.020 rows=1
loops=1)
                  Index Cond:  (labor_uid = 20178)


So this is the case then you could do all of your sorting before the
join takes place by using a sub query. This would also mean you could
do your LIMIT and OFFSET in the same sub query. If the tables in the
sub query were properly indexed then this could be a a pretty fast
operation which would only hit less than 1 million rows. I'm not quite
sure how your partitioning is setup though as the names seem to
indicate both dates and labour UIDs... so I'll just ignore these for
now...

The query I came up with looked like this:


 SELECT *

 FROM labor_tasks labor_1

 INNER JOIN (SELECT *

             FROM labor_task_report this_

             WHERE labor_UID = 20178

             ORDER BY work_DATE_TIME asc, work_UID asc, task_REPORT_UID

             LIMIT 10000 OFFSET 940000

) this_ ON labor_1.labor_uid = this_.labor_uid

ORDER BY this_.work_DATE_TIME asc, this.work_UID asc, this.task_REPORT_UID

If you had an index on labor_UID, work_date_time, work_UID,
task_Report_uid then inner would have a better chance at running
quickly.


Though I may have mis-read the explain analyze results and have it
completely wrong.

Postgresql is a pretty amazing feat of engineering, but it does lack
sometimes when it comes to properly optimising queries to the most
optimal way by detecting functional dependencies in tables to speed up
joins and grouping.

Regards

David





>
>
>
> --
> Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-performance
>
>

^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: Poor OFFSET performance in PostgreSQL 9.1.6
@ 2013-08-29 11:00  hubert depesz lubaczewski <depesz@depesz.com>
  parent: fburgess@radiantblue.com
  3 siblings, 0 replies; 6+ messages in thread

From: hubert depesz lubaczewski @ 2013-08-29 11:00 UTC (permalink / raw)
  To: fburgess@radiantblue.com; +Cc: pgsql-performance

On Wed, Aug 28, 2013 at 01:39:46PM -0700, fburgess@radiantblue.com wrote:
>    Can anyone offer suggestions on how I can optimize a query that contains the LIMIT OFFSET clause?
>    The explain plan of the query is included in the notepad attachment.
>    thanks

large offsets are slow, and there is no real escape from it.

You can change your paging solution, though, to something that will be
faster.

Possible solutions/optimizations:

http://www.depesz.com/2007/08/29/better-results-paging-in-postgresql-82/

or

http://www.depesz.com/2011/05/20/pagination-with-fixed-order/

Best regards,

depesz



-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance



^ permalink  raw  reply  [nested|flat] 6+ messages in thread


end of thread, other threads:[~2013-08-29 11:00 UTC | newest]

Thread overview: 6+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2013-08-28 20:39 Poor OFFSET performance in PostgreSQL 9.1.6 fburgess@radiantblue.com
2013-08-28 21:26 ` Greg Spiegelberg <gspiegelberg@gmail.com>
2013-08-28 23:10 ` Merlin Moncure <mmoncure@gmail.com>
2013-08-29 04:43 ` David Rowley <dgrowleyml@gmail.com>
2013-08-29 11:00 ` hubert depesz lubaczewski <depesz@depesz.com>
2013-08-28 22:08 Re: Poor OFFSET performance in PostgreSQL 9.1.6 fburgess@radiantblue.com

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox