数据库出问题,网站就瘫。而数据库故障往往是突发且影响大的:连接数打满、查询突然变慢、磁盘一夜之间满了、表损坏了。
这篇按故障现象组织,每种问题给出排查步骤和解决办法。
数据库故障最好的恢复手段是备份。财富云计算 的云服务器支持快照与备份;内部系统、测试环境可选 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; -- 互相等待解决
- 统一加锁顺序:所有事务按相同顺序操作表
- 缩短事务:减少事务里的语句,尽快提交
- 降低隔离级别:用 READ COMMITTED 代替 REPEATABLE READ
- 重试机制:程序捕获死锁错误后自动重试几次
- 加索引:减少锁的范围(没索引会锁更多行)
锁等待超时
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.MYIInnoDB 表:
InnoDB 有崩溃恢复机制,一般重启会自动恢复。若不行:
# 强制恢复模式(1-6,逐级尝试,越大越危险)
innodb_force_recovery = 1systemctl 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_Running | IO 线程,应为 Yes |
Slave_SQL_Running | SQL 线程,应为 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 -urootFLUSH 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 / 内存 / 磁盘 / 带宽》
- 《服务器备份与快照策略》
- 《网站打开慢的排查流程》
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]# - buffer pool 命中率(应 >99%)一次只改一个变量,改完观察一天。批量改索引,出了问题不知道回滚哪个。六、速查:EXPLAIN 结果对照表看到动作type=ALL 且 rows 很大加索引Extra=Using filesort索引带上排序字段Extra=Using temporary优化 GROUP BY,索引配合key=NULLWHERE 条件列没索引rows 远[...]
2026-10-15 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复