N1QL Pagination Configuration for Deep Offset Performance
0 reputation · 10 Dec 2020, 12:49 UTC
0 reputation · 10 Dec 2020, 12:49 UTC
Couchbase SQL++ (N1QL) employs OFFSET and LIMIT clauses to manage result set pagination. The query engine processes these by scanning and discarding all documents preceding the specified offset before returning the target page.
In environments utilizing the Java SDK, this behavior can lead to linear performance degradation as the offset increases, potentially causing high latency or timeouts on the Index and Query services. While keyset pagination using range filters on sorted indexes is a documented alternative to avoid these scans, it shifts the state management to the application layer.
What are the specific performance thresholds where OFFSET becomes unsustainable compared to keyset pagination? Are there Query service configuration settings available to optimize the discard phase of an OFFSET operation?
34275 reputation · 10 Dec 2020, 20:54 UTC
OFFSET becomes unsustainable when the number of rows skipped approaches a noticeable fraction of the total result set, causing linear scan time that grows with offset.
The discard phase cannot be avoided by Query service settings; it is intrinsic to the OFFSET operator. Settings such as pipeline_batch, pipeline_cap, or query_timeout only affect how results are buffered or how long a request may run, not the amount of work needed to skip rows.
CREATE INDEX idx_order ON `bucket`(field1, field2, field3) WHERE condition;
system:active_requests.SELECT ... FROM `bucket` WHERE (field1, field2, field3) > (last_f1, last_f2, last_f3) ORDER BY field1, field2, field3 LIMIT $pageSize;
N1qlQueryParameterized with consistent placeholders.query_timeout in the Query service config if you must retain OFFSET for very large offsets, but note that this only hides the symptom; the underlying scan cost remains.EXPLAIN SELECT ... to confirm the index is used and that no extra filter step appears.system:active_requests for elapsedTime and scanWait fields to see if scan wait grows with offset.To refine the threshold recommendation, please provide the approximate total number of documents in the bucket (or the cardinality of the indexed ORDER BY fields). This lets us calculate whether your typical offset values fall within the 1 % / 10 000‑row safe zone.
Use comments to ask for clarification. Post a solution as an answer.
34,275 reputation · 10 Dec 2020, 16:43 UTC
While keyset pagination resolves the linear performance decay of OFFSET, its reliability depends entirely on the uniqueness of the sort key. If the ORDER BY clause uses a non-unique field (such as a timestamp or status code), documents with identical values may be skipped or duplicated across page boundaries.
To ensure a stable seek, the sort criteria must be deterministic. This is typically achieved by appending the document metadata ID as a tie-breaker in both the index and the query:
SELECT ... FROM `bucket`
WHERE (field1, META().id) > ($last_f1, $last_id)
ORDER BY field1, META().id
LIMIT $pageSize;
When verifying this implementation, use the EXPLAIN statement to confirm that the query engine is performing an Index Scan rather than a Fetch for the discarded rows, which validates that the seek is occurring directly at the index level.