Skip to content

15|常见故障排查

前面 14 篇讲了怎么装、怎么用、怎么优化。但实际运维里 MySQL 出问题,往往就是几个固定的类型:起不来、连不上、磁盘满、字符集乱、跑得慢。

本篇把这些常见故障的排查套路集中讲。复制异常在第 13 篇、慢查询在第 7 篇讲过,这里不重复,聚焦启动、连接、磁盘、字符集这几类。

一、错误日志是第一站

不管什么故障,先看错误日志。它比 systemd 的状态更具体:

sql
SHOW VARIABLES LIKE 'log_error';
bash
tail -n 200 /data/mysql/logs/error.log

# systemd 环境
journalctl -u mysqld -n 100 --no-pager

systemd 只显示 Failed with exit-code,错误日志里能看到具体原因——unknown variablePermission deniedbind on TCP/IP port failed先看错误日志,再动手。

二、启动失败

systemctl start mysqld 失败。常见原因和排查:

原因错误日志特征排查
配置参数写错unknown variable 'xxx'检查 my.cnf 拼写
目录权限不对Permission denied看数据目录属主
数据目录不存在datadir 相关错误确认 datadir 路径
端口被占用bind on TCP/IP port failedss -lntp | grep 3306
磁盘满No space left on devicedf -h + df -ih
redo/undo 损坏错误日志有具体信息看日志判断能不能修

检查关键路径和权限:

bash
# 看配置里的路径
grep -E 'datadir|log_error|socket|pid-file|tmpdir' /etc/my.cnf

# 确认目录权限(mysql 用户要有读写权限)
ls -ld /data/mysql /data/mysql/data /data/mysql/logs

# 修正权限
chown -R mysql:mysql /data/mysql

# 端口是否被占
ss -lntp | grep ':3306'

chown -R 前确认路径别打错——打错了 chown -R mysql:mysql / 就是灾难。先 pwdls -ld 确认再执行。

磁盘满要分两种:df -h 看数据盘空间,df -ih 看 inode 是否用完(小文件太多会耗尽 inode,空间还有但建不了文件)。

三、连接失败

连不上 MySQL。分几层排查:

bash
# 1. 网络层——端口通不通
nc -vz mysql_host 3306
ss -lntp | grep ':3306'    # 服务端看端口在不在

# 2. 本地 socket 连(绕过网络层)
mysql -uroot -p --socket=/data/mysql/tmp/mysql.sock

socket 能连但 TCP 不能——bind_address 限制了监听地址:

sql
SHOW VARIABLES LIKE 'bind_address';

默认 * 监听所有地址。如果配成了 127.0.0.1,远程连不上——只允许本机。

Access denied

连上了但认证失败:

text
ERROR 1045 (28000): Access denied for user 'app'@'10.0.1.5'

排查方向:

  • 密码对不对
  • 账号存在不存在:SELECT user, host FROM mysql.user WHERE user='app'
  • host 匹配对不对(第 11 篇讲过 user@host 匹配)
  • 账号有没有被锁:account_locked

第 11 篇讲的 USER() vs CURRENT_USER() 在这里用——看实际匹配到哪个账号。

Too many connections

text
ERROR 1040 (HY000): Too many connections

连接数打满。第 10 篇讲过——不是无脑调大 max_connections,要看连上来的人在干什么。但当下连不进去处理不了,可以留一个管理连接:

ini
# my.cnf
max_connections=500
admin_port=33062        # 额外管理端口,不占普通连接配额

admin_port 是给 root 的专用连接端口,连接打满时也能连进去排查。

四、磁盘空间

磁盘满会导致 MySQL 写不了、起不来。常见占用大户:

bash
# 看数据目录占用
du -sh /data/mysql/* | sort -hr

# 看 binlog 占了多少
du -sh /data/mysql/logs/mysql-bin*

# 看慢日志、错误日志
du -sh /data/mysql/logs/*.log

binlog 是常见占用大户——binlog_expire_logs_seconds 设太长,binlog 堆着不清。看 binlog 列表和最早时间:

sql
SHOW BINARY LOGS;

清理过期的(确认从库不需要了才删):

sql
PURGE BINARY LOGS BEFORE '2026-06-01';

或者调短保留时间(重启后对新增 binlog 生效):

ini
binlog_expire_logs_seconds=259200    # 3 天

别在生产主库乱 RESET MASTER——会清掉所有 binlog,从库复制直接断。

五、字符集问题

存中文或 emoji 出现乱码、???、或存不进去报错:

sql
-- 看库的字符集
SELECT default_character_set_name FROM information_schema.schemata WHERE schema_name='app_db';

-- 看表的字符集
SELECT table_name, table_collation FROM information_schema.tables WHERE table_schema='app_db';

-- 看字段的字符集
SELECT column_name, character_set_name FROM information_schema.columns WHERE table_name='servers';

字符集不一致是乱码根因——库是 utf8mb4,表是 latin1,或连接客户端用的字符集和库不一致。

连接字符集:

sql
SHOW VARIABLES LIKE 'character_set%';

character_set_clientcharacter_set_connectioncharacter_set_results 这几个要一致,通常都是 utf8mb4。连接时指定:

bash
mysql --default-character-set=utf8mb4 -uroot -p

或在 my.cnf:

ini
[client]
default-character-set=utf8mb4

[mysqld]
character_set_server=utf8mb4

全文 utf8mb4 统一——库、表、字段、连接,全用 utf8mb4,不要混 utf8 和 utf8mb4(第 1 篇讲过 utf8 是残缺的)。

已有乱码数据的处理

已经存了乱码的数据不好直接修。要先确认数据是怎么存进去的(什么字符集写入、什么字符集读出),再用 CONVERT 转换。乱码数据往往是"写入时字符集不对",转换要对症。预防胜于治疗——新库一开始就统一 utf8mb4。

六、现场保存

出大故障时,在动手修之前先保存现场——日志、进程状态、连接列表。修的过程中状态会变,没保存就回溯不了:

bash
# 保存错误日志
cp /data/mysql/logs/error.log /tmp/error-$(date +%F-%H%M%S).log

# 保存进程列表
mysql -uroot -p -e "SHOW FULL PROCESSLIST" > /tmp/processlist-$(date +%F-%H%M%S).txt

# 保存复制状态
mysql -uroot -p -e "SHOW REPLICA STATUS\G" > /tmp/replica-$(date +%F-%H%M%S).txt

出了事故要复盘时,这些现场数据是依据。先存再修,别等修完发现要查原因时啥都没了。

排查的共同思路

不管哪种故障:

  1. 先看错误日志——它比任何猜测都准
  2. 分清故障类型——启动、连接、磁盘、字符集,排查方向不同
  3. 保存现场再动手——便于复盘
  4. 查配置和权限——大量故障是配置写错或权限不对
  5. 对照正常状态——不知道正不正常时,和一台正常的 MySQL 对比

系列收尾

MySQL 系列 15 篇走完了。从装一个 MySQL 开始,到 SQL、索引、InnoDB、事务、性能、账号、备份、复制、高可用、故障排查。这套覆盖了一个 MySQL 运维该掌握的核心。

记住一条主线:MySQL 出问题,先看错误日志,再分清故障类型,按类型走对应排查路径。 不靠猜,靠日志和数据。