Databases 13 min read

Why Large OFFSETs Slow Down MySQL Queries and How to Fix Them

This article analyzes a production incident where massive OFFSET values in MySQL pagination caused severe query slowdown, demonstrates the problem with simulated data, benchmarks the impact, and presents three practical solutions—including index‑covering subqueries, remembering the last primary key, and limiting offset size—to dramatically improve performance and protect the database.

Architecture & Thinking
Architecture & Thinking
Architecture & Thinking
Why Large OFFSETs Slow Down MySQL Queries and How to Fix Them

Background

On January 22, after work, a developer called about a production issue: an API was being invoked with an extremely large offset and limit, causing the MySQL cluster to slow down.

The request URL (with domain, module, and method placeholders) used offset=1800000 and limit=500 , which corresponds to page 3601, while the UI shows only 25 rows per page. Over 8,000 such calls were observed, likely from a multithreaded client named ApiAutotest generated by a QA account.

Analysis

The query itself is well‑optimized with proper indexes, but using a large OFFSET forces MySQL to scan and discard millions of rows. Example:

SELECT * FROM t_name WHERE c_name1='xxx' ORDER BY c_name2 LIMIT 2000000,25;

This forces the engine to read 2,000,025 rows and then return only the last 25, which is extremely inefficient. As the offset grows, the query becomes progressively slower, especially beyond millions of rows.

Data Simulation

To reproduce the issue, two tables ( emp and dep) are created. Random‑string and random‑number functions are defined, and stored procedures insert 5,000,000 employee rows and 120 department rows. Indexes on primary key and foreign key columns are added after data generation.

Test

Two queries are executed:

Offset 100, limit 25 – completes instantly.

Offset 4,800,000, limit 25 – takes about 12.275 seconds.

Solution

1. Index covering + subquery optimization

Fetch the starting id with a subquery and then retrieve the next rows, e.g.:

SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
FROM emp a LEFT JOIN dep b ON a.depno=b.depno
WHERE a.id >= (SELECT id FROM emp ORDER BY id LIMIT 100,1)
ORDER BY a.id LIMIT 25;

This reduces the execution time for the large‑offset query to roughly 0.106 seconds.

2. Redefine start position

Remember the last fetched primary‑key value and query with WHERE a.id > last_id instead of using OFFSET. This yields constant‑time performance (≈0 seconds) regardless of page number.

3. Degradation strategy

Set a maximum allowed offset; requests exceeding this threshold return empty results or a 4xx error, preventing massive scans and protecting the database.

Conclusion

Applying the first two solutions and limiting offset size dramatically improves query performance and safeguards the system from abuse. Rate‑limiting and throttling should also be considered for high‑frequency calls.

Pagination performance illustration
Pagination performance illustration
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.

performancesqlindexingmysql
Architecture & Thinking
Written by

Architecture & Thinking

🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.

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.