The table has a primary key. Make use of it.
Instead of LIMIT and OFFSET, do your paging with a filter on the primary key. You hinted at this with your comment:
Paging using random numbers ( Add "GREATER THAN ORDER BY " to each
query )
but there's nothing random about how you should do it.
SELECT * FROM big_table WHERE id > $1 ORDER BY id ASC LIMIT $2
Allow the client to specify both parameters, the last ID it saw and the number of records to fetch. Your API will have to either have a placeholder, extra parameter, or alternate call for "fetch the first n IDs" where it omits the WHERE clause from the query, but that's trivial.
This approach will use a fairly efficient index scan to get the records in order, generally avoiding a sort or the need to iterate through all the skipped records. The client can decide how many rows it wants at once.
This approach differs from the LIMIT and OFFSET approach in one key way: concurrent modification. If you INSERT into the table with a key lower than a key some client has already seen, this approach will not change its results at all, whereas the OFFSET approach will repeat a row. Similarly, if you DELETE a row with a lower-than-already-seen ID the results of this approach will not change, whereas OFFSET will skip an unseen row. There is no difference for append-only tables with generated keys, though.
If you know in advance that the client will want the whole result set, the most efficient thing to do is just send them the whole result set with none of this paging business. That's where I would use a cursor. Read the rows from the DB and send them to the client as fast as the client will accept them. This API would need to set limits on how slow the client was allowed to be to avoid excessive backend load; for a slow client I'd probably switch to paging (as described above) or spool the whole cursor result out to a temporary file and close the DB connection.
Important caveats:
- Requires a
UNIQUE constraint / UNIQUE index or PRIMARY KEY to be reliable
- Different concurrent modification behaviour to limit/offset, see above