tags:

views:

72

answers:

2

A table with about 70K records is displayed on a site, showing 50 records per page. Pagination is done with limit offset,50 on the query, and the records can be ordered on different columns.

Browsing the latest pages (so the offset is around 60,000) makes the queries much slower than when browsing the first pages (about 10x)

Is this an issue of using the limit command? Are there other ways to get the same results?

+2  A: 

It's how MySQL deals with limits. If it can sort on an index (and the query is simple enough) it can stop searching after finding the first offset + limit rows. So LIMIT 0,10 means that if the query is simple enough, it may only need to scan 10 rows. But LIMIT 1000,10 means that at minimum it needs to scan 1010 rows. Of course, the actual number of rows that need to be scanned depend on a host of other factors. But the point here is that the lower the limit + offset, the lower that the lower-bound on the number of rows that need to be scanned is...

As for workarounds, I would optimize your queries so that the query itself without the LIMIT clause is as efficient as possible. EXPLAIN is you friend in this case...

ircmaxell
+6  A: 

With large offsets, MySQL needs to browse more records.

Even if the plan uses filesort (which means that all records should be browsed), MySQL optimizes it so that only $offset + $limit top records are sorted, which makes it much more efficient for lower values of $offset.

The typical solution is to index the columns you are ordering on, record the last value of the columns and reuse it in the subsequent queries, like this:

SELECT  *
FROM    mytable
ORDER BY
        value, id
LIMIT 0, 10

which outputs:

value  id

1      234
3      57
4      186
5      457
6      367
8      681
10     366
13     26
15     765
17     345  -- this is the last one

To get to the next page, you would use:

SELECT  *
FROM    mytable
WHERE   (value, id) > (17, 345)
ORDER BY
        value, id
LIMIT 0, 10

, which uses the index on (value, id).

Of course this won't help with arbitrary access pages, but helps with sequential browsing.

Also, MySQL has certain issues with late row lookup. If the columns are indexed, it may be worth trying to rewrite your query like this:

SELECT  *
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                value, id
        LIMIT   $offset, $limit
        ) q
JOIN    mytable m
ON      m.id = q.id

See this article for more detailed explanations:

Quassnoi
MySQL supports that tuple-style syntax (`(val1, val2) > (1, 2)`)? I've never seen that before. What's the combiner (AND, I'm assuming)? I guess you learn something new every day...
ircmaxell
@ircmaxell: yes it does. It's lexicographical order, the same as `val1 > 1 OR (val1 = 1 AND val2 > 2)`
Quassnoi
Good answer. Here is a presentation that I think has some good info that is along the same lines: http://www.slideshare.net/Eweaver/efficient-pagination-using-mysql
nathan
I've compared the old and the new query, the one with the join is two to three times faster.Thanks!As a side effect, I've noticed that in some cases I could reduce the number of columns on the `order by` without notable differences, and an additional speed improvement.
UVL