- pagination (slow, not recommended)
- Keyset pagination ( fast, recommended )
- (it depends, see Differences between keyset pagination and cursors )
Keyset pagination
Keyset pagination (also known as the “seek method”) is used to fetch a subset of records from a table quickly. It does this by restricting the set of records returned with a combination ofWHERE and clauses. To get the next page, you check the value of the column in the WHERE clause against the last row returned in the previous page of results.
The general pattern for keyset pagination queries is:
LIMIT/OFFSET because, instead of doing a full table scan up to the value of the OFFSET, a keyset pagination query looks at a fixed-size set of records for each iteration. This can be done quickly provided that the key used in the WHERE clause to implement the pagination is and . A meets both of these criteria.
Examples
The examples in this section use the employees data set, which you can load into CockroachDB as follows:We use in these examples to ensure that we are operating on a consistent snapshot of the database as of the specified timestamp. This reduces the chance that there will be any concurrent updates to the data the query is accessing, and thus no missing or duplicated rows during the pagination. It also reduces the risk of due to concurrent data access. The value of
-1m passed to AS OF SYSTEM TIME may need to be updated depending on your application’s data access patterns.emp_no) are in a much higher range, run the following query. Note that it takes about the same amount of time to run as the previous queries.
LIMIT / OFFSET to get the same page of results:
LIMIT/OFFSET for pagination is almost 100 times slower. To see why, let’s use .
LIMIT/OFFSET, we are performing a limited scan of the entire table (see spans: LIMITED SCAN above) from the first record all the way up to the value of the offset. In other words, we are iterating over a big array of rows from 1 to n, where n is 200049. The estimated row count row shows this.
Meanwhile, the keyset pagination queries are looking at a much smaller range of table spans, which is much faster (see spans: [/300026 - ] and limit: 25 below). Because , these queries are doing an index lookup to jump to the start of the page of results, and then getting an additional 25 rows from there. This is much faster.
estimated row count row, this query scans only 25 rows, far fewer than the 200049 scanned by the LIMIT/OFFSET query.
Using a sequential (i.e., non-) primary key creates hot spots in the database for write-heavy workloads, since concurrent s to the table will attempt to write to the same (or nearby) underlying . This can be mitigated by designing your schema with .
Differences between keyset pagination and cursors
Cursors are stateful objects that use more database resources than keyset pagination, since each cursor holds open a transaction. However, they are easier to use, and make it easier to get consistent results without having to write complex queries from your application logic. They do not require that the results be returned in a particular order (that is, you don’t have to include anORDER BY clause), which makes them more flexible.
Keyset pagination queries are usually much faster than cursors since they order by indexed columns. However, in order to get that performance they require that you return results in some defined order that can be calculated by your application’s queries. Because that ordering involves calculating the start/end point of pages of results based on an indexed key, they require more care to write correctly.
See also
- pagination (slow, not recommended)

