Databases 14 min read

How to Fix Common MySQL Performance Pitfalls: Real-World SQL Optimizations

This article examines frequent MySQL performance problems such as inefficient LIMIT usage, implicit type conversion, subquery updates, mixed ordering, EXISTS clauses, predicate push‑down, early row reduction, and intermediate result set handling, providing concrete rewrites and execution‑plan comparisons that dramatically improve query speed.

ITPUB
ITPUB
ITPUB
How to Fix Common MySQL Performance Pitfalls: Real-World SQL Optimizations

Introduction

MySQL continues to grow in popularity; many customers experience slow response times or high CPU usage. This article summarizes common SQL problems from ApsaraDB expert diagnosis reports and shows how to rewrite them for better performance.

Common SQL Mistakes

1. LIMIT clause

Pagination often uses LIMIT offset, count. A small offset works with a composite index, but a large offset such as LIMIT 1000000,10 forces the engine to scan from the beginning, causing severe slowdown. Rewriting the query to filter by the last seen value keeps execution time constant.

SELECT * FROM operation
WHERE type='SQLStats' AND name='SlowLog' AND create_time > '2017-03-16 14:00:00'
ORDER BY create_time
LIMIT 10;

2. Implicit type conversion

When a column type differs from the literal, MySQL converts the column to a number, which disables index usage. The example below compares a VARCHAR(20) column with a numeric literal, generating a warning and a full‑table scan.

SELECT * FROM my_balance b
WHERE b.bpn = 14000000123 AND b.isverified IS NULL;

3. Update/Delete with subqueries

MySQL 5.6’s materialized‑subquery optimization applies only to SELECT. An UPDATE … IN (SELECT …) becomes a DEPENDENT SUBQUERY, which is slow. Rewriting it as a JOIN changes the plan to DERIVED and reduces execution time from seconds to milliseconds.

UPDATE operation o
JOIN (
    SELECT o.id
    FROM operation o
    WHERE o.group = 123 AND o.status NOT IN ('done')
    ORDER BY o.parent, o.id
    LIMIT 1
) t ON o.id = t.id
SET o.status = 'applying';

4. Mixed ordering

MySQL cannot use an index for mixed ASC/DESC ordering. By separating rows according to is_reply and using UNION ALL, the query runs in about 2 ms instead of 1.58 s.

SELECT * FROM (
    SELECT * FROM my_order o
    INNER JOIN my_appraise a ON a.orderid = o.id
    WHERE is_reply = 0
    ORDER BY appraise_time DESC
    LIMIT 0,20
) UNION ALL
SELECT * FROM (
    SELECT * FROM my_order o
    INNER JOIN my_appraise a ON a.orderid = o.id
    WHERE is_reply = 1
    ORDER BY appraise_time DESC
    LIMIT 0,20
) ORDER BY is_reply ASC, appraisetime DESC
LIMIT 20;

5. EXISTS clause

The EXISTS clause is executed as a nested subquery. Replacing it with an INNER JOIN removes the dependent subquery and cuts execution time from ~2 s to 1 ms.

SELECT *
FROM my_neighbor n
INNER JOIN message_info m ON n.id = m.neighbor_id AND m.inuser = 'xxx'
LEFT JOIN my_neighbor_apply sra ON n.id = sra.neighbor_id AND sra.user_id = 'xxx'
WHERE n.topic_status < 4
  AND n.topic_type <> 5;

6. Predicate push‑down

Conditions cannot be pushed into complex views or subqueries such as aggregated subqueries, LIMIT subqueries, UNION subqueries, or subqueries in the SELECT list. Moving the predicate before the subquery changes the plan from DEPENDENT SUBQUERY to SIMPLE, dramatically improving performance.

SELECT target, COUNT(*)
FROM operation
WHERE target = 'rm-xxxx'
GROUP BY target;

7. Early row reduction

When WHERE and ORDER BY apply to the leftmost table, sorting that table first and then joining reduces the number of rows processed. The rewritten query drops execution time from 12 s to about 1 ms.

SELECT *
FROM (
    SELECT *
    FROM my_order o
    WHERE o.display = 0 AND o.ostaus = 1
    ORDER BY o.selltime DESC
    LIMIT 0,15
) o
LEFT JOIN my_userinfo u ON o.uid = u.uid
LEFT JOIN my_productinfo p ON o.pid = p.pid
ORDER BY o.selltime DESC
LIMIT 0,15;

8. Pushing intermediate result sets

For a query that joins a filtered subquery with an aggregated subquery, moving the aggregation into a WITH clause and joining only the needed keys reduces runtime from seconds to milliseconds.

WITH a AS (
    SELECT resourceid
    FROM my_distribute
    WHERE isdelete = 0 AND cusmanagercode = '1234567'
    ORDER BY salecode
    LIMIT 20
)
SELECT a.*, c.allocated
FROM a
LEFT JOIN (
    SELECT resourcesid, SUM(IFNULL(allocation,0)*12345) AS allocated
    FROM my_resources r
    WHERE r.resourcesid = a.resourcesid
    GROUP BY resourcesid
) c ON a.resourceid = c.resourcesid;

Conclusion

Database compilers generate execution plans that heavily influence performance. Understanding their behavior and avoiding common pitfalls—large offsets, implicit conversions, subquery patterns, lack of predicate push‑down, and inefficient ordering—allows developers to write efficient SQL, reduce load on cloud databases, and achieve millisecond‑level response 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.

mysqlindexesSQL OptimizationQuery PlanningSubqueries
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

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.