13 10 月 2026

数据库出问题,网站就瘫。而数据库故障往往是突发且影响大的:连接数打满、查询突然变慢、磁盘一夜之间满了、表损坏了。

这篇按故障现象组织,每种问题给出排查步骤和解决办法。

数据库故障最好的恢复手段是备份。财富云计算 的云服务器支持快照与备份;内部系统、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上)。

一、连接数打满(Too many connections)

现象

ERROR 1040 (HY000): Too many connections

网站报错、数据库连不上,但 MySQL 进程还在。

排查

SHOW VARIABLES LIKE 'max_connections';       -- 上限
SHOW STATUS LIKE 'Threads_connected';        -- 当前连接数
SHOW STATUS LIKE 'Max_used_connections';     -- 历史峰值
SHOW PROCESSLIST;                            -- 看连接都在干什么
-- 按用户统计
SELECT user, host, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY user, host;

-- 看有多少是 Sleep(空闲连接)
SELECT command, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY command;

原因与解决

① 连接泄漏(程序没正确关闭连接)

表现为大量 Sleep 状态连接且时间很长。

-- 查看 Sleep 超过 100 秒的连接
SELECT * FROM information_schema.PROCESSLIST
WHERE command = 'Sleep' AND time > 100;

应急:缩短超时,让空闲连接自动断开:

SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;

永久配置:

wait_timeout = 300
interactive_timeout = 300

根治:修程序,确保连接用完释放(用连接池)。

② 慢查询堵塞

大量连接卡在某个慢查询上。

SELECT * FROM information_schema.PROCESSLIST
WHERE command != 'Sleep' AND time > 5 ORDER BY time DESC;

找到卡住的 SQL,KILL 掉并优化。

③ 真的并发量大

SET GLOBAL max_connections = 500;    -- 临时调大
max_connections = 500                -- 永久

注意:每个连接占几 MB 内存,别设太大。真正解决要靠连接池和缓存。

④ 应急:不重启也能连进去

MySQL 保留了 1 个给 root 的连接(extra_port / extra_max_connections)。用 root 从本地连:

mysql -uroot -p --protocol=socket

或者临时用 gdb 修改(不推荐)。更实际的做法:先杀掉一批空闲连接。

# 批量杀 Sleep 连接
mysql -uroot -p -e "SELECT CONCAT('KILL ', id, ';') FROM information_schema.PROCESSLIST WHERE command='Sleep' AND time > 300;" | mysql -uroot -p

二、查询突然变慢

排查流程

-- ① 看当前在跑什么
SHOW PROCESSLIST;

-- ② 看慢日志
-- 命令行:tail -100 /var/log/mysql/slow.log

-- ③ 分析具体 SQL
EXPLAIN SELECT ...;

常见原因

① 索引失效

数据量增长后,原本走索引的查询变成全表扫描。

EXPLAIN SELECT * FROM orders WHERE status = 1;
-- type=ALL, rows=500000 → 需要加索引
ALTER TABLE orders ADD INDEX idx_status (status);
ANALYZE TABLE orders;      -- 更新统计信息

② 统计信息过期

优化器选错了执行计划。

ANALYZE TABLE tablename;

③ 表碎片过多

频繁更新删除后,表有大量碎片。

-- 查看碎片
SELECT table_name, data_free
FROM information_schema.tables
WHERE table_schema = 'mydb' AND data_free > 0;

OPTIMIZE TABLE tablename;      -- 会锁表,业务低峰做

④ 锁等待

SHOW ENGINE INNODB STATUS\G      -- 看 TRANSACTIONS 部分
SELECT * FROM information_schema.INNODB_LOCKS;      -- MySQL 5.7
SELECT * FROM performance_schema.data_locks;        -- MySQL 8.0

⑤ 磁盘 IO 打满

iostat -x 1       # %util 接近 100% 说明磁盘瓶颈

解决:加内存(加大 buffer pool)、换 SSD、优化查询减少 IO。

⑥ 临时表落盘

SHOW STATUS LIKE 'Created_tmp_disk_tables';    -- 在磁盘建临时表次数
SHOW STATUS LIKE 'Created_tmp_tables';

若磁盘临时表比例高,调大:

tmp_table_size = 128M
max_heap_table_size = 128M

三、死锁

现象

ERROR 1213 (40001): Deadlock found when trying to get lock

排查

SHOW ENGINE INNODB STATUS\G
-- 看 LATEST DETECTED DEADLOCK 部分,会显示两个事务各自的 SQL

常见原因

两个事务以相反顺序加锁:

-- 事务 A
UPDATE t1 SET ... WHERE id=1;
UPDATE t2 SET ... WHERE id=2;

-- 事务 B
UPDATE t2 SET ... WHERE id=2;
UPDATE t1 SET ... WHERE id=1;     -- 互相等待

解决

  1. 统一加锁顺序:所有事务按相同顺序操作表
  2. 缩短事务:减少事务里的语句,尽快提交
  3. 降低隔离级别:用 READ COMMITTED 代替 REPEATABLE READ
  4. 重试机制:程序捕获死锁错误后自动重试几次
  5. 加索引:减少锁的范围(没索引会锁更多行)

锁等待超时

SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';   -- 默认 50 秒
SET GLOBAL innodb_lock_wait_timeout = 30;

查看谁在阻塞:

-- MySQL 8.0
SELECT waiting_pid, waiting_query, blocking_pid, blocking_query
FROM sys.innodb_lock_waits;

四、磁盘空间暴涨

排查

# 看 MySQL 数据目录
du -sh /var/lib/mysql/
du -sh /var/lib/mysql/*

# 看各库大小
mysql -e "SELECT table_schema, ROUND(SUM(data_length+index_length)/1024/1024,2) MB
FROM information_schema.tables GROUP BY table_schema ORDER BY MB DESC;"

常见元凶

① 二进制日志(binlog)

ls -lh /var/lib/mysql/mysql-bin.*     # 或 /var/log/mysql/
-- 查看
SHOW BINARY LOGS;

-- 清理 7 天前的
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);

-- 清理到指定文件
PURGE BINARY LOGS TO 'mysql-bin.000123';

自动清理:

expire_logs_days = 7                       # MySQL 5.7/MariaDB
binlog_expire_logs_seconds = 604800        # MySQL 8.0

② 慢日志 / 错误日志

du -sh /var/log/mysql/*
# 清理慢日志
> /var/log/mysql/slow.log

③ 临时文件

SHOW VARIABLES LIKE 'tmpdir';
du -sh /tmp/

大查询排序会产生临时文件。

④ 表碎片

SELECT table_name, data_free/1024/1024 AS free_mb
FROM information_schema.tables
WHERE table_schema='mydb' AND data_free > 104857600
ORDER BY data_free DESC;
OPTIMIZE TABLE 大表名;     -- 低峰期执行,会锁表

⑤ undo 表空间(MySQL 8)

长事务会导致 undo 无法回收,文件一直涨。

-- 查看长事务
SELECT * FROM information_schema.innodb_trx
ORDER BY trx_started LIMIT 5;

杀掉长事务,或配置自动 truncate:

innodb_undo_log_truncate = 1
innodb_max_undo_log_size = 1G

五、表损坏

现象

Table './mydb/xxx' is marked as crashed and should be repaired

或查询报错、数据读不出来。

排查

CHECK TABLE tablename;
# 查看错误日志
tail -100 /var/log/mysql/error.log

解决

MyISAM 表(老式存储引擎):

REPAIR TABLE tablename;
# 或命令行
myisamchk -r /var/lib/mysql/mydb/tablename.MYI

InnoDB 表:

InnoDB 有崩溃恢复机制,一般重启会自动恢复。若不行:

# 强制恢复模式(1-6,逐级尝试,越大越危险)
innodb_force_recovery = 1
systemctl restart mysql
# 能起来后立刻 mysqldump 导出数据
# 然后重建表/重装,再导入

⚠️ innodb_force_recovery 大于 4 可能导致数据丢失,务必先备份数据目录。

预防:

  • 用 UPS/确保正常关机
  • 定期检查
  • 有备份

六、内存占用过高

排查

free -h
top -p $(pgrep mysqld)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Innodb_buffer_pool_pages%';

原因

① buffer_pool 设置过大

innodb_buffer_pool_size = 512M     # 按内存调小

② 连接数太多

每个连接占 sort_buffer + join_buffer + read_buffer + 临时表。

估算:max_connections × (sort_buffer + join_buffer + read_buffer + read_rnd_buffer)

连接多时这些 per-connection 的 buffer 要调小:

sort_buffer_size = 1M
join_buffer_size = 1M
read_buffer_size = 512K

③ 被 OOM 杀掉

dmesg | grep -i "out of memory"
grep -i "killed process" /var/log/syslog

解决:调小 buffer_pool、限制连接数、加内存、或给 MySQL 加 systemd 内存限制。


七、主从同步问题(如有)

SHOW SLAVE STATUS\G       -- MySQL 5.7
SHOW REPLICA STATUS\G     -- MySQL 8.0

关注:

字段含义
Slave_IO_RunningIO 线程,应为 Yes
Slave_SQL_RunningSQL 线程,应为 Yes
Seconds_Behind_Master延迟秒数
Last_Error错误信息

常见错误:

-- 错误 1062(主键冲突)
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter = 1;
START SLAVE;

-- 或跳过指定错误
[mysqld]
slave_skip_errors = 1062

延迟大:从库性能不足、大事务、表无主键(会导致全表扫描应用 binlog)。


八、无法启动

排查

systemctl status mysql
tail -100 /var/log/mysql/error.log
journalctl -u mysql -n 100

常见原因

报错原因解决
Can't create/write to file目录权限chown -R mysql:mysql /var/lib/mysql
Address already in use端口被占`ss -tulnp \grep 3306`,停掉占用的
Table 'mysql.user' doesn't exist数据目录损坏从备份恢复或初始化
InnoDB: Operating system error number 13权限问题检查数据目录权限
unknown variable配置文件写错检查最近改的配置
磁盘满—清理空间
mysqld: Can't find file: './mysql/plugin.frm'数据目录不对检查 datadir 配置

应急:跳过授权表启动(忘记 root 密码时):

systemctl stop mysql
mysqld_safe --skip-grant-tables &
mysql -uroot
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';

然后正常重启。


九、慢查询应急处理

线上突然变慢,按这个顺序:

-- ① 看当前活跃连接
SELECT id, user, host, db, command, time, state, LEFT(info, 100) AS sql
FROM information_schema.PROCESSLIST
WHERE command != 'Sleep' AND time > 2
ORDER BY time DESC;
-- ② 杀掉最慢的几个(谨慎!确认是能杀的)
KILL 12345;
-- ③ 看是否有锁等待
SHOW ENGINE INNODB STATUS\G
# ④ 看系统资源
top
iostat -x 1
-- ⑤ 临时缓解:加大 buffer pool(如果内存够)
SET GLOBAL innodb_buffer_pool_size = ...;   -- MySQL 5.7.5+ 支持在线调整
-- ⑥ 临时缓解:杀掉空闲连接
SELECT CONCAT('KILL ',id,';') FROM information_schema.PROCESSLIST
WHERE command='Sleep' AND time>300;

十、日常巡检清单

#!/bin/bash
# /usr/local/bin/mysql-check.sh
MYSQL="mysql -uroot"

echo "===== MySQL 巡检 ====="
echo "--- 运行状态 ---"
systemctl is-active mysql

echo "--- 连接数 ---"
$MYSQL -e "SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';"

echo "--- 慢查询数 ---"
$MYSQL -e "SHOW STATUS LIKE 'Slow_queries';"

echo "--- 当前活跃查询 ---"
$MYSQL -e "SELECT COUNT(*) FROM information_schema.PROCESSLIST WHERE command!='Sleep';"

echo "--- 数据库大小 ---"
$MYSQL -e "SELECT table_schema, ROUND(SUM(data_length+index_length)/1024/1024,2) MB FROM information_schema.tables GROUP BY table_schema ORDER BY MB DESC;"

echo "--- 数据目录占用 ---"
du -sh /var/lib/mysql/

echo "--- 最近错误 ---"
tail -20 /var/log/mysql/error.log 2>/dev/null

每周跑一次,或配到监控系统里。


十一、预防措施

  • [ ] 开启慢查询日志,定期分析
  • [ ] 关键查询都 EXPLAIN 过,有索引
  • [ ] 配置合理的 buffer_pool 和 max_connections
  • [ ] 自动备份 + 定期恢复演练
  • [ ] 监控:连接数、慢查询数、磁盘空间、QPS
  • [ ] 设置 binlog 过期自动清理
  • [ ] 程序使用连接池,及时释放连接
  • [ ] 大表变更(ALTER)在低峰期做
  • [ ] 定期检查表(CHECK TABLE)

数据库出问题影响最大,备份和监控不能省。财富云计算 的云服务器支持快照,可在做危险操作(比如 OPTIMIZE 大表、升级)前打快照。内部系统、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上),数据库只监听本地,安全性更好。

相关阅读:

  • 《MySQL 安装与优化入门》
  • 《服务器资源排查:CPU / 内存 / 磁盘 / 带宽》
  • 《服务器备份与快照策略》
  • 《网站打开慢的排查流程》
延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:MySQL / MariaDB 安装与优化入门:让数据库跑得动
  2. 下一篇:Redis 安装与缓存应用实战:给数据库减负的最快手段
发表评论 2