MySQL Debugging: Common Problems and Solutions#
When MySQL breaks, it falls into a handful of failure modes. Here are the diagnostic workflows, in order of frequency.
Access Denied Errors#
Access denied for user 'appuser'@'10.0.1.5' (using password: YES) means wrong password, user does not exist for that host, or missing privileges.
Diagnosis:
-- 1. Does the user exist for that host?
SELECT user, host, plugin FROM mysql.user WHERE user = 'appuser';
-- MySQL matches user+host pairs. 'appuser'@'localhost' != 'appuser'@'%'.
-- 2. Check grants
SHOW GRANTS FOR 'appuser'@'%';
-- 3. Auth plugin mismatch? Old clients can't handle caching_sha2_password:
ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'password';To reset a lost root password:
# Stop MySQL, start with --skip-grant-tables
sudo systemctl stop mysqld
sudo mysqld --skip-grant-tables --skip-networking &
mysql -u rootFLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password';Then restart MySQL normally.
Too Many Connections#
Error 1040: Too many connections. MySQL hit max_connections.
-- Check current state
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
-- See what connections are doing
SHOW FULL PROCESSLIST;
-- Look for: many connections in Sleep state (idle app connections),
-- or many connections in Query state (overloaded server)Immediate fix:
-- Increase dynamically (does not persist across restart)
SET GLOBAL max_connections = 500;The real question is why. Common causes: no connection pooling, idle connections not being closed, or burst traffic. Find and kill idle connections:
SELECT id, user, host, command, time FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300;
KILL 12345;Long-term: implement connection pooling (ProxySQL, HikariCP) and set wait_timeout to close idle connections automatically.
Slow Queries and SHOW PROCESSLIST#
When the application is slow, start here:
-- What is running right now?
SHOW FULL PROCESSLIST;
-- Look for queries with high Time values or State = 'Sending data',
-- 'Creating sort index', 'Copying to tmp table'
-- For more detail, use Performance Schema
SELECT * FROM performance_schema.events_statements_current
WHERE SQL_TEXT IS NOT NULL\GIf you see a specific slow query, grab it and run EXPLAIN:
EXPLAIN ANALYZE <paste the slow query here>;Common patterns:
type: ALLon a large table: missing index.Using temporary; Using filesort: the query needs a temp table and sort. Consider adding an index that covers the ORDER BY/GROUP BY.- Subquery in WHERE clause scanning millions of rows: rewrite as a JOIN.
InnoDB Lock Waits#
Lock wait timeout exceeded; try restarting transaction – one transaction is waiting for a lock held by another.
-- MySQL 8.0+: find who is blocking whom
SELECT * FROM performance_schema.data_lock_waits\G
SELECT * FROM performance_schema.data_locks\G
-- Deeper analysis
SHOW ENGINE INNODB STATUS\G
-- Look for "LATEST DETECTED DEADLOCK" and "TRANSACTIONS" sectionsFind the blocking transaction’s thread ID and kill it if necessary:
KILL 67890;Common causes: long-running transactions left open (forgot to COMMIT), batch UPDATEs hitting many rows while OLTP queries run, and missing indexes causing lock escalation.
Replication Lag#
On a replica:
SHOW REPLICA STATUS\G
-- Key fields:
-- Seconds_Behind_Source: lag in seconds (NULL means replication is broken)
-- Replica_IO_Running: should be Yes
-- Replica_SQL_Running: should be Yes
-- Last_Error: error message if SQL thread stoppedIf Replica_SQL_Running: No, check Last_Error. For duplicate key errors, skip the event: SET GLOBAL sql_replica_skip_counter = 1; START REPLICA;
If lag is high but replication is running, enable parallel replication:
SET GLOBAL replica_parallel_workers = 4;
SET GLOBAL replica_parallel_type = 'LOGICAL_CLOCK';Other causes: heavy writes exceeding replica disk throughput, or long queries on the replica blocking the apply thread.
Disk Full#
MySQL stops accepting writes when disk is full. The error log will show No space left on device.
df -h /var/lib/mysql
du -sh /var/lib/mysql/*
# Purge binary logs
mysql -e "PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;"
# Rotate slow log
mv /var/log/mysql/slow.log /var/log/mysql/slow.log.old && mysqladmin flush-logs
# Check for long-running transactions bloating undo logs
mysql -e "SELECT trx_id, trx_started, trx_rows_modified FROM information_schema.innodb_trx;"Crashed Tables and InnoDB Corruption#
If MySQL won’t start or you see InnoDB: corrupted page in the error log:
# Check the error log first
tail -100 /var/log/mysql/error.log
# Start in recovery mode (try levels 1-6, start low)
# Add to my.cnf:
# [mysqld]
# innodb_force_recovery = 1
sudo systemctl start mysqld
# Once started, dump the data immediately
mysqldump -u root -p --all-databases > emergency_dump.sql
# Then rebuild: stop MySQL, remove data, reinitialize, restore
sudo systemctl stop mysqld
sudo rm -rf /var/lib/mysql/*
sudo mysqld --initialize
sudo systemctl start mysqld
mysql -u root -p < emergency_dump.sqlRecovery levels: 1 (skip corrupt pages) through 6 (skip redo log). Higher levels risk data loss. Always dump and rebuild rather than running in recovery mode permanently.
For individual InnoDB table corruption, force a rebuild:
ALTER TABLE tablename ENGINE=InnoDB; -- rebuilds the table in placeThis resolves minor corruption and reclaims fragmented space. REPAIR TABLE only works for MyISAM.