Racket/db LIMIT/OFFSET pagination: performance uncertainty with large offsets
0 reputation · 22 Jun 2024, 12:22 UTC
0 reputation · 22 Jun 2024, 12:22 UTC
Assess whether using large OFFSET values in query calls from the racket/db library causes measurable performance regressions across common back‑ends (SQLite, PostgreSQL, MySQL). The library passes raw SQL strings with LIMIT and OFFSET unchanged, but the effect of high offset values on driver and engine execution plans is unclear.
1. The dataset size is expected to be in the hundreds of thousands. 2. Offset values may exceed 50 000 or more. 3. The same SQL text is sent to different DBMS drivers, each with its own execution strategy for OFFSET. 4. The racket/db library does not provide a built‑in keyset pagination abstraction, so developers must decide between offset‑based paging or manual WHERE‑clause pagination.
racket/db forward LIMIT and OFFSET verbatim to SQLite, PostgreSQL, and MySQL drivers, or are there subtle differences?OFFSET exceeds 100 000 rows on each back‑end, and how does it compare to keyset pagination using a unique ordered column?31775 reputation · 22 Jun 2024, 17:20 UTC
Racket/db is a thin wrapper around database drivers. When you call query or query-rows with a SQL string that contains LIMIT and OFFSET, the library passes that string to the underlying driver with essentially no transformation. Therefore the pagination semantics and cost are determined entirely by the database engine, not by Racket code. This means the performance characteristics you observe are the same as if you ran the raw SQL directly on the DBMS.
Offset‑based pagination requires the engine to scan and discard the skipped rows before returning the requested page. On common back‑ends this cost grows roughly linearly with the offset value:
In practice, on a dataset of a few hundred thousand rows, offsets exceeding 50 000–100 000 often produce measurable slowdown compared with the first page, especially when the sort column lacks a selective index.
Keyset pagination replaces OFFSET with a WHERE clause that references the last seen value of a monotonically ordered column. This turns the query into an index range scan, whose cost is roughly constant regardless of page depth.
Racket/db has no built‑in keyset helper, but the common practice is to construct the SQL manually using parameterized placeholders. Example for a table with a primary‑key id:
(query db
"SELECT * FROM items WHERE id > $1 ORDER BY id LIMIT $2"
last-seen-id page-size)
Keep the SQL text stable across calls and vary only the bound values; racket/db’s prepared‑statement caching avoids re‑parsing overhead.
If the sort column is not unique, include a tie‑breaker (e.g., (created_at, id)) and ensure a composite index exists.
To tailor the recommendation, please provide the database engine version and average row size (or typical column count).
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.