Databases 10 min read

How to Speed Up MySQL Pagination: Practical Optimizations and Benchmarks

This article examines why naïve LIMIT‑OFFSET pagination on a multi‑million‑row MySQL table becomes increasingly slow, presents several optimization techniques—including simple LIMIT usage, sub‑query id lookup, ID‑range queries, and temporary‑table tricks—along with concrete benchmark results to guide developers toward faster data paging.

Java Backend Technology
Java Backend Technology
Java Backend Technology
How to Speed Up MySQL Pagination: Practical Optimizations and Benchmarks

Preparation

When a table contains millions of rows, retrieving all records at once is very slow, especially as the data volume grows. The article uses a table order_history (37 columns, primary key id, 5,709,294 rows, MySQL 5.7.16) for testing.

General Pagination with LIMIT

The basic pagination uses the LIMIT clause:

SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset;

Key points:

The first parameter is the offset (starting from 0).

The second parameter is the maximum number of rows to return.

If only one parameter is given, it specifies the row count.

A second parameter of -1 means return all rows from the offset to the end.

Initial offset is 0, not 1.

Example:

SELECT * FROM orders_history WHERE type=8 LIMIT 1000,10;

This returns rows with id between 1001 and 1010.

Benchmark (three runs) for different LIMIT values:

1 row: ~3 000 ms

10 rows: ~3 050 ms

100 rows: ~3 150 ms

1 000 rows: ~3 450 ms

10 000 rows: ~3 800 ms

When the record count is below 100, query time is almost unchanged; larger result sets increase latency noticeably.

Impact of Offset

Tests with increasing offsets show a sharp rise in execution time once the offset exceeds 100 000 rows.

Offset 100: ~25 ms

Offset 1 000: ~77 ms

Offset 10 000: ~3 100 ms

Offset 100 000: ~3 800 ms

Offset 1 000 000: ~14 500 ms

Because MySQL scans from the first row, larger offsets cause slower queries.

Sub‑query Optimization

First locate the offset position's id, then fetch rows based on that id. Suitable when id is auto‑incrementing.

SELECT * FROM orders_history WHERE type=8 LIMIT 100000,1;
SELECT id FROM orders_history WHERE type=8 LIMIT 100000,1;
SELECT * FROM orders_history WHERE type=8 AND id >= (SELECT id FROM orders_history WHERE type=8 LIMIT 100000,1) LIMIT 100;

Benchmark:

Statement 1 (SELECT *): 3 674 ms

Statement 2 (SELECT id): 1 315 ms

Statement 3 (SELECT * with sub‑query id): 1 327 ms

Statement 4 (original LIMIT): 3 710 ms

Using SELECT id instead of SELECT * speeds up the query by about three times.

ID‑Range Optimization

If id values are continuous, calculate the range for the desired page and query with BETWEEN:

SELECT * FROM orders_history WHERE type=2 AND id BETWEEN 1000000 AND 1000100 LIMIT 100;

Execution time: 9‑15 ms.

Another form:

SELECT * FROM orders_history WHERE id >= 1000001 LIMIT 100;

Using IN with a sub‑query is also possible, but some MySQL versions do not allow LIMIT inside the IN clause.

Temporary Table Optimization

When id is not strictly continuous (e.g., historical tables or missing data), store the pagination id list in a temporary table and query with IN. This can dramatically improve performance for tables with tens of millions of rows.

Notes on Table IDs

It is common practice to add an auto‑incrementing id column to every table for easy querying. For very large tables (e.g., order databases), sharding is often used, and a distributed unique ID generator should be employed instead of the native auto‑increment key.

Overall, locating the id (or another indexed column) first and then fetching the full rows can speed up pagination by several times.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performancesqlmysqlDatabase Optimization
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.