Skip to content

MySQL Tutorial Query Performance and Optimization

The Slow Query Log

Lesson 209 of 308 4 min read By Deepak Paswan Updated Oct 5, 2026

You cannot fix slow queries you cannot see. The slow query log records every statement that takes longer than a threshold, together with its execution time, the rows examined and the rows sent.

The settings

VariableMeaningDefault
slow_query_logSwitch the log on or offOFF
long_query_timeLog statements slower than this many seconds10
log_outputWhere to write: FILE, TABLE or bothFILE
slow_query_log_fileThe file name (for FILE)host-slow.log
log_queries_not_using_indexesAlso log queries that use no indexOFF
min_examined_row_limitDo not log statements that examine fewer rows0
log_slow_extraWrite extra fields (more details) to the fileOFF
log_slow_admin_statementsAlso log slow ALTER, OPTIMIZE, ANALYZEOFF
query.sql
SQL
SELECT @@slow_query_log AS enabled, @@long_query_time AS threshold_seconds, @@log_output AS destination;
Output
OUTPUT
+---------+-------------------+-------------+
| enabled | threshold_seconds | destination |
+---------+-------------------+-------------+
|       0 |         10.000000 | FILE        |
+---------+-------------------+-------------+
1 row in set

Switch it on

For a running server (until the next restart):

query.sql
SQL
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;              -- seconds. Try 0.5 or lower on busy systems
SET GLOBAL log_queries_not_using_indexes = 0; -- turn it on only for short investigations

To make it permanent, put it in the configuration file (my.cnf or my.ini):

my.cnf
INI
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_slow_admin_statements = 1
info
Session vs global
SET GLOBAL long_query_time applies only to new connections. An already open connection keeps its own session value. After you change the global value, reconnect, or set SET SESSION long_query_time in the connection you test with.

Write the log to a table (easy to query)

With log_output = 'TABLE', slow statements go into the table mysql.slow_log, which you can query with SQL. Here the threshold is set to 0 for this one session, so even fast statements are logged:

query.sql
SQL
TRUNCATE TABLE mysql.slow_log;
SET GLOBAL log_output = 'TABLE';
SET GLOBAL slow_query_log = 1;
SET SESSION long_query_time = 0;

SELECT SLEEP(0.2);
SELECT 'marker query' AS x;

SET GLOBAL slow_query_log = 0;
SET GLOBAL log_output = 'FILE';

SELECT query_time > 0.1 AS slow, rows_sent, rows_examined, CONVERT(sql_text USING utf8mb4) AS sql_text
FROM mysql.slow_log
WHERE CONVERT(sql_text USING utf8mb4) LIKE '%marker query%' OR CONVERT(sql_text USING utf8mb4) LIKE '%SLEEP(0.2)%'
ORDER BY sql_text
LIMIT 3;
Output
OUTPUT
+------------+
| SLEEP(0.2) |
+------------+
|          0 |
+------------+
1 row in set

+--------------+
| x            |
+--------------+
| marker query |
+--------------+
1 row in set

+------+-----------+---------------+----------------------------+
| slow | rows_sent | rows_examined | sql_text                   |
+------+-----------+---------------+----------------------------+
|    0 |         1 |             1 | SELECT 'marker query' AS x |
|    1 |         1 |             1 | SELECT SLEEP(0.2)          |
+------+-----------+---------------+----------------------------+
2 rows in set

(Verified on a real server. The table log is convenient for exploring, but a file log is faster and is what most tools read. Keep log_output = FILE on production.)

What a file entry looks like

Text
PLAIN
# Time: 2026-07-15T10:21:08.123456Z
# User@Host: app[app] @ 10.0.0.12 []  Id:  4521
# Query_time: 2.314159  Lock_time: 0.000112  Rows_sent: 20  Rows_examined: 3180442
SET timestamp=1784110868;
SELECT id, title FROM posts WHERE LOWER(title) LIKE '%mysql%' ORDER BY created_at DESC LIMIT 20;

The numbers to look at:

  • Query_time: how long it took.
  • Rows_examined vs Rows_sent: the query looked at 3.18 million rows to return 20. A huge ratio means missing indexes or a poor query shape.
  • Lock_time: time waiting for locks.

Summarise the log

A log of thousands of entries is useless to read by eye. Group the entries by query shape (the same statement with different values):

mysqldumpslow (comes with MySQL):

terminal
BASH
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
  • -s t: sort by total time (c count, a average time, r rows).
  • -t 10: show the top 10.

pt-query-digest (Percona Toolkit) gives a much richer report, with the share of total time of each query shape, percentiles and an example of each:

terminal
BASH
pt-query-digest /var/log/mysql/mysql-slow.log | head -100

The first entries of its report are the queries that cost the most in total, which are the ones to fix first.

What threshold?

  • Start at long_query_time = 1 second to find the worst queries.
  • Then lower it to 0.5, 0.2 and further, as you fix things.
  • For a short investigation, set it to 0 for a few minutes to capture all queries, then analyse (the file grows quickly).
  • Use min_examined_row_limit to ignore tiny queries.

Queries without indexes

log_queries_not_using_indexes = 1 logs every statement that does a full scan, even if it is fast. On small tables, this creates a lot of noise. Turn it on for a short time on a representative environment, and combine it with min_examined_row_limit = 1000.

Cost of logging

Logging to a file is cheap. Logging every statement on a very busy server (threshold 0) writes a lot of data, so do it for minutes, not days. Rotate the log files (logrotate, or FLUSH SLOW LOGS).

Beyond the log

The slow log shows what was slow in the past. For a live view, use performance_schema and the sys schema (Performance Schema and sys), and SHOW FULL PROCESSLIST for what is running now.

lightbulb
Total time beats single slowest
A query that takes 20 ms and runs 500,000 times a day costs more than one that takes 5 seconds and runs twice. Sort by total time, not just by the slowest single run.