Databases 16 min read

Why Is MySQL Query Slow? Hidden Factors and Proven Speed‑Up Tricks

This article explains why MySQL queries can become sluggish beyond missing indexes, covering the full query execution flow, profiling tools, index pitfalls, connection‑pool limits, InnoDB buffer‑pool sizing, and practical steps to diagnose and accelerate database performance.

NiuNiu MaTe
NiuNiu MaTe
NiuNiu MaTe
Why Is MySQL Query Slow? Hidden Factors and Proven Speed‑Up Tricks

Database Query Process

When a client issues a SQL statement, MySQL goes through connection management, parsing, optimization, execution, and finally the storage engine to retrieve rows.

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT 'name',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT 'age',
  `gender` int(8) NOT NULL DEFAULT '0' COMMENT 'gender',
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`),
  KEY `idx_gender` (`gender`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

The client (e.g., Go or C++) opens a TCP long‑connection, sends the SQL, and MySQL parses it, checks syntax, then the optimizer chooses an index, and the executor calls the storage engine.

MySQL architecture
MySQL architecture

Slow‑Query Analysis

Enable profiling to see where time is spent.

mysql> set profiling=ON;
mysql> show profiles;
+----------+------------+---------------------------------------------------+
| Query_ID | Duration   | Query                                             |
+----------+------------+---------------------------------------------------+
| 1        | 0.06811025 | select * from user where age>=60                 |
| 2        | 0.00151375 | select * from user where gender=2 and age=80    |
| 3        | 0.00230425 | select * from user where gender=2 and age=60    |
| 4        | 0.00070400 | select * from user where gender=2 and age=100 |
| 5        | 0.07797650 | select * from user where age!=60                |
+----------+------------+---------------------------------------------------+

Use show profile for 1 to get a breakdown of each stage; the "Sending data" phase often dominates when many rows are returned.

Index‑Related Causes

EXPLAIN reveals which index is used and how many rows are scanned.

EXPLAIN SELECT * FROM user WHERE age>=60;
+----+-------------+-------+------+---------------+------+---------+------+-------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | Extra |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------+
|  1 | SIMPLE      | user  | ALL  | idx_age       | NULL | NULL    | NULL | 100000| NULL  |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------+

If the optimizer chooses a full table scan, it may be because the index is non‑selective or requires a costly table‑lookup (back‑table).

Index Not as Expected

When many similar indexes exist, MySQL may pick the wrong one. Use FORCE INDEX (idx_age) to force the desired index.

Force index
Force index

Index Still Slow

Low selectivity (e.g., short prefix indexes) or a large number of matching rows can make an index slower than a full scan.

Low index selectivity
Low index selectivity

Connection Count Too Small

MySQL’s default max_connections is 100; increase it with:

mysql> set global max_connections=500;
mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 500   |
+-----------------+-------+

Application‑side connection pools (e.g., GORM) also need enough connections:

func Init() {
    db, err := gorm.Open(mysql.Open(conn), config)
    sqlDB, err := db.DB()
    sqlDB.SetMaxIdleConns(200) // idle connections
    sqlDB.SetMaxOpenConns(1000) // max open connections
}

Buffer Pool Size

InnoDB’s buffer pool caches data pages; a larger pool improves hit rate.

mysql> show global variables like 'innodb_buffer_pool_size';
+--------------------------+-----------+
| Variable_name            | Value     |
+--------------------------+-----------+
| innodb_buffer_pool_size  | 134217728 |
+--------------------------+-----------+

Increase it, e.g., to 512 MiB, and monitor hit rate with

show status like 'Innodb_buffer_pool_%';
buffer pool hit rate = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100%

Other Tricks

MySQL 8.0 removed the query cache; relying on it is not recommended.

Summary

Slow queries are usually index‑related: wrong index or too many rows.

Insufficient client or server connections limit concurrency; raise max_connections and pool size.

InnoDB buffer‑pool hit rate > 99 % is ideal; increase size if the rate drops.

Query cache can speed up reads but is deprecated and removed in MySQL 8.0.

indexingConnection PoolQuery OptimizationMySQLbuffer pool
NiuNiu MaTe
Written by

NiuNiu MaTe

Joined Tencent (nicknamed "Goose Factory") through campus recruitment at a second‑tier university. Career path: Tencent → foreign firm → ByteDance → Tencent. Started as an interviewer at the foreign firm and hopes to help others.

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.