M4RKYU.SYSEdition 2027
Skip to content
LOCEN/Ontario · CA/▸logs · mysql performance monitoring and query analysis 2coStandbyOK/--:--:--EST
M4M4RK_YUportfolio
  • BuildBuild
    BuildOverview
    • WorkSelected case studies and write-ups
    • GamesPlayable prototypes and game-dev logs
  • GalleryGallery
    GalleryOverview
    • PhotosPhoto collections and visual experiments
    • ShopPrints, posters, and one-off objects
  • WritingWriting
    WritingOverview
    • BlogLong-form devlogs and field notes
    • NotesShort observations, links, snippets
  • ResourcesResources
    ResourcesOverview
    • Tools38 in-browser developer utilities
    • LinksDaily-use dev and design bookmarks
  • AboutAbout
  • ContactContact
中文

syndicated · dev.to / @markyu

Debug a Slow MySQL Query Before You Guess at Indexes

A practical MySQL workflow for finding slow queries, reading EXPLAIN output, and deciding whether an index actually helps.

Published
May 20 '24
·
Reading time
3 min read
·
Reactions
13
·
Comments
2
mysqldatabasebackendperformance
View on dev.toDiscuss

On this page

  • 1. Confirm MySQL Is Actually the Bottleneck
  • 2. Turn On the Slow Query Log
  • 3. Summarize the Worst Queries
  • 4. Run EXPLAIN on the Real Query
  • 5. Add the Index That Matches the Query
  • 6. Do Not Keep Every Index
  • 7. The Mistakes I See Most
  • My Checklist

The fastest way to make MySQL worse is to add indexes because a query "feels slow."

Start with evidence. Find the query. Measure it. Read the plan. Then change one thing.

Here is the workflow I use.

1. Confirm MySQL Is Actually the Bottleneck

Do not start inside EXPLAIN. First check if the database is under obvious stress.

SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW GLOBAL STATUS LIKE 'Innodb_rows_read';
SHOW GLOBAL STATUS LIKE 'Com_select';

These numbers do not solve the problem, but they tell you where to look.

If Slow_queries is climbing during the incident, turn to the slow query log. If connections are spiking, you may have an app pooling problem. If rows read is exploding, you probably have missing selectivity or a bad access pattern.

2. Turn On the Slow Query Log

Check the current setting:

SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'slow_query_log_file';

Enable it for the running server:

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;

For persistent config, add this to your MySQL config:

slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 0

I usually avoid turning on log_queries_not_using_indexes at first. It can create noise. A query can skip indexes and still be fine on a small table. Start with actual slow queries.

3. Summarize the Worst Queries

Use mysqldumpslow when you need a quick local read:

mysqldumpslow -s t -t 10 /var/lib/mysql/*-slow.log

Useful sort modes:

mysqldumpslow -s t -t 10 /var/lib/mysql/*-slow.log  # total query time
mysqldumpslow -s at -t 10 /var/lib/mysql/*-slow.log # average query time
mysqldumpslow -s c -t 10 /var/lib/mysql/*-slow.log  # count

I care about two classes:

  • A query that runs rarely but takes forever.
  • A query that is individually mediocre but runs thousands of times.

Both can hurt production.

4. Run EXPLAIN on the Real Query

Say the slow log points to this:

SELECT id, title, published_at
FROM posts
WHERE author_id = 42
  AND status = 'published'
ORDER BY published_at DESC
LIMIT 20;

Run:

EXPLAIN
SELECT id, title, published_at
FROM posts
WHERE author_id = 42
  AND status = 'published'
ORDER BY published_at DESC
LIMIT 20;

The columns I check first:

ColumnWhat I look for
typeALL means full scan. Not always bad, but suspicious on large tables.
possible_keysIndexes MySQL could use.
keyThe index MySQL actually used.
rowsEstimated rows scanned.
ExtraWatch for Using filesort and Using temporary.

5. Add the Index That Matches the Query

For the query above, this is a practical index:

CREATE INDEX idx_posts_author_status_published
ON posts (author_id, status, published_at DESC);

Why this order?

  • author_id is an equality filter.
  • status is another equality filter.
  • published_at supports the sort after filtering.

Then run EXPLAIN again. If rows drops and the sort gets cheaper, you probably helped.

6. Do Not Keep Every Index

Indexes speed reads but slow writes and consume memory/disk. Every new index should pay rent.

Check existing indexes:

SHOW INDEX FROM posts;

If two indexes overlap, you may not need both.

Example:

INDEX (author_id)
INDEX (author_id, status, published_at)

The longer composite index can often satisfy lookups that start with author_id. Do not delete blindly, but do question duplicates.

7. The Mistakes I See Most

Adding an index on the wrong single column:

CREATE INDEX idx_posts_status ON posts (status);

If almost every row is published, this index is low value.

Using functions on indexed columns:

WHERE DATE(published_at) = '2026-06-16'

Prefer a range:

WHERE published_at >= '2026-06-16'
  AND published_at < '2026-06-17'

Selecting more columns than needed:

SELECT * FROM posts ...

Make the query say what the screen actually needs.

My Checklist

  • Find the slow query from logs, not vibes.
  • Reproduce the query with realistic parameters.
  • Run EXPLAIN.
  • Add one index that matches filters and sorting.
  • Run EXPLAIN again.
  • Measure the endpoint again.
  • Remove indexes that do not earn their cost.

Performance work is not magic. It is a loop: observe, change, measure.

Related reading

Bad Data Quality Costs More Than a Slow Query

A practical data quality guide for engineers: validation, ownership, schema drift, observability, and fixing bad data before dashboards lie.

data

Java BeanUtils Copying: Convenient, but Not Free

A practical Java guide to BeanUtils, shallow copy pitfalls, reflection overhead, and when MapStruct or manual mapping is a better choice.

java

RedisJSON Is Useful When You Update Parts of a Document

A practical RedisJSON walkthrough: when to use it, when not to, and the commands that actually matter.

redis

originally published

This post first ran on dev.to. Comments and reactions live there.

Continue on dev.to
PreviousCSS 3D Transform Bugs Usually Come From PerspectiveA practical CSS 3D transform guide explaining perspective, rotateX, rotateY, transform-style, backface visibility, and debugging layout.
Back to all posts
NextCSS Heart Animation: Small Demo, Real Animation LessonsA cleaner CSS heart animation tutorial focused on transform, pseudo-elements, keyframes, and the small mistakes that break simple UI animations.
Back to archive
M4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYU
Crafted since 2024
ZhenXiao Mark YuZhenXiao Mark Yu
get in touch

Saw something here?Tell me about it.

It's a portfolio, not a service · but I read every note — drop a line if anything here resonated, or just to say hi.

Start a conversation
open channel

say hi anytime · 2026

--:--:--ESTOntario, Canada
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat

Newsletter

Get the occasional dispatch

Notes and logs from m4rkyu.com — short, dated, no noise. Unsubscribe anytime.

Work

Production builds, games, and visual archives.

  • Projects
  • Games
  • Archive
  • Logs

Resources

Daily-use tools and a personal link library.

  • Search
  • Latest
  • Tools
  • Links
  • Notes
  • Topics
  • Shop
RSSJSON feed

Studio

Background, contact, and channels for collaboration.

  • About
  • Contact
  • Changelog
  • Colophon
  • Resumepending

Socials

Find me on the usual feeds.

  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
  • Email
© 2026 ZhenXiao Mark Yumarkyu0615@gmail.com
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
PrivacyTermsBuilt with Next.js 16 · React 19 · Tailwind 4