MySQL / Redis 常见故障排查速查清单
来源:运维派 | 发布日期:2026-07-04
覆盖 MySQL 和 Redis 最常见的 20+ 种生产故障,每个故障按「现象 → 排查 → 根因 → 修复 → 验证」结构组织。适合值班 oncall、故障复盘、健康巡检时快速对号入座。
💡 本文是"速查清单/决策树"视角。MySQL 性能调优(慢查询/锁/死锁/索引/Buffer Pool)深度内容见 mysql-performance-config;Redis 内存优化(数据结构/TTL/淘汰/Bigkey/碎片)见 redis-memory-optimization;Redis 连接管理见 redis-connection-management。
整体排查框架
无论是 MySQL 还是 Redis 故障:
告警 → 确认影响范围 → 收集关键指标 → 缩小范围 → 定位根因 → 修复 → 验证 → 复盘
关键原则:先止血,再排查。kill 进程前先记录 processlist,清理数据前先备份。不确定的命令先在测试环境验证。
核心排查工具速查
| MySQL | 用途 | Redis | 用途 |
|---|---|---|---|
SHOW PROCESSLIST |
当前连接和 SQL | INFO |
全局信息(内存/连接/复制/持久化) |
SHOW ENGINE INNODB STATUS |
死锁/IO/Buffer Pool | SLOWLOG GET |
慢查询日志 |
SHOW SLAVE STATUS |
主从复制状态 | CLIENT LIST |
当前所有客户端 |
performance_schema / sys |
锁等待/IO/语句统计 | MEMORY USAGE key |
单个 key 内存占用 |
慢查询日志 + mysqldumpslow |
慢 SQL 分析 | redis-cli --bigkeys |
大 key 扫描 |
pt-query-digest |
慢 SQL 深度分析 | LATENCY HISTORY |
延迟事件历史 |
information_schema |
元数据(表大小/索引) | MEMORY DOCTOR |
内存问题诊断 |
第一部分:MySQL 常见故障
故障 1:连接数耗尽
现象:应用报 ERROR 1040 (HY000): Too many connections
排查:
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
-- 按用户/主机聚合,找连接泄漏源
SELECT user, SUBSTRING_INDEX(host,':',1) AS client_ip, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY user, client_ip ORDER BY cnt DESC LIMIT 20;
-- 查看空闲连接(可能连接泄漏)
SELECT id, user, host, db, command, time
FROM information_schema.processlist
WHERE command = 'Sleep' ORDER BY time DESC LIMIT 20;
根因判断:Threads_connected 高但 Threads_running 低 → 空闲连接堆积。Threads_running 也高 → 慢查询/锁等待阻塞。某 IP 异常高 → 该应用实例问题。
修复:紧急 SET GLOBAL max_connections = 2000;;短期 kill 空闲连接 + 调 wait_timeout;长期修复连接泄漏。
故障 2:主从复制延迟
现象:Seconds_Behind_Master 持续增大或波动
排查:
-- MySQL 5.7: SHOW SLAVE STATUS\G
-- MySQL 8.0+: SHOW REPLICA STATUS\G
-- 关注:Seconds_Behind_Master、IO/SQL 线程状态、Relay_Log_Space
-- 查看从库正在回放什么
SHOW PROCESSLIST;
-- 是否有锁等待阻塞回放
SELECT * FROM information_schema.innodb_trx;
根因:主库大事务 → 从库单线程回放慢;从库磁盘 IO 瓶颈(iostat 确认);从库查询锁住回放;并行复制未开启。
修复:开启并行复制(replica_parallel_type=LOGICAL_CLOCK);避免主库大事务,拆分为小批量;从库使用 SSD。
故障 3:慢查询拖垮系统
现象:CPU 飙高,大量查询超时
排查:
-- 查看当前正在执行的慢 SQL
SELECT id, user, host, db, time, state, LEFT(info, 200) AS query
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 3 ORDER BY time DESC;
# 分析慢查询日志
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
pt-query-digest /var/log/mysql/slow.log --limit=20
修复:添加索引;优化 SQL(减少子查询、避免 SELECT *、合理分页);kill 超时 SQL(先确认影响);临时限流有问题接口。
故障 4:死锁
现象:Deadlock found when trying to get lock; try restarting transaction
排查:
SHOW ENGINE INNODB STATUS\G
-- 找到 LATEST DETECTED DEADLOCK 部分
-- 显示:参与死锁的两个事务 / 各自持有和等待的锁 / 被回滚的事务(victim)
-- 开启死锁日志记录到错误日志(MySQL 5.7.2+)
SET GLOBAL innodb_print_all_deadlocks = ON;
根因:两个事务以不同顺序更新同一组行;间隙锁冲突(RR 隔离级别下的范围查询);唯一索引冲突 + INSERT。
修复:统一加锁顺序(按主键顺序操作);减小事务粒度;考虑降级到 RC 隔离级别;应用层加重试。
故障 5:元数据锁等待(MDL Lock)
现象:SHOW PROCESSLIST 显示大量 Waiting for table metadata lock
排查:
-- 需要先开启 instrument
UPDATE performance_schema.setup_instruments
SET enabled = 'YES', timed = 'YES'
WHERE name = 'wait/lock/metadata/sql/mdl';
-- 查看 MDL 锁持有情况
SELECT * FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
-- 查看长事务(可能持有 MDL 共享锁)
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query
FROM information_schema.innodb_trx ORDER BY trx_started ASC;
根因:未提交的长事务持有 MDL 读锁 → DDL 无法获取写锁 → 后续所有查询排队。
修复:kill 持有 MDL 的长事务(先确认 innodb_trx.trx_rows_modified,大事务回滚耗时);DDL 使用 pt-online-schema-change 或 gh-ost;大表 DDL 安排在低峰期。
故障 6:磁盘写满
现象:MySQL 无法写入,df -h 显示 100%
排查:
df -h
du -sh /var/lib/mysql/* | sort -rh | head -20
ls -lh /var/lib/mysql/mysql-bin.* # binlog 占用
常见根因:binlog 无过期时间持续堆积;大事务 undo log 膨胀(ibdata1 增长);慢查询大量临时文件;通用日志误开启。
修复:
-- MySQL 8.0: 设置 binlog 7 天过期
SET GLOBAL binlog_expire_logs_seconds = 604800;
-- MySQL 5.7: SET GLOBAL expire_logs_days = 7;
-- 手动清理 binlog(确认从库已同步)
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);
⚠️
PURGE BINARY LOGS不可逆。ibdata1 增大后不会自动缩小,只能逻辑导出导入重建。
故障 7:复制中断
现象:SQL 线程停止,Last_Error 不为空
| 错误码 | 含义 | 修复 |
|---|---|---|
| 1062 Duplicate entry | 从库已有该数据 | SET GLOBAL sql_slave_skip_counter = 1;(跳过后用 pt-table-checksum 校验) |
| 1032 Can't find record | 从库找不到要更新/删除的行 | 同上,跳过 + 校验 |
| 1236 binlog position error | binlog 被清理或损坏 | 重建从库(xtrabackup/mysqldump) |
故障 8:Buffer Pool 命中率低
现象:查询响应变慢,磁盘 IO 升高
排查:
SHOW STATUS LIKE 'Innodb_buffer_pool_read_requests'; -- 逻辑读
SHOW STATUS LIKE 'Innodb_buffer_pool_reads'; -- 物理读
-- 命中率 = 1 - (reads / read_requests),正常应 > 99%
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Innodb_buffer_pool_pages_free';
修复:调大 innodb_buffer_pool_size(建议物理内存 60%-80%);优化 SQL 避免全表扫描;用 innodb_old_blocks_time 保护热数据。
故障 9:临时表/排序溢出到磁盘
现象:EXPLAIN 显示 Using temporary; Using filesort
排查:
SHOW STATUS LIKE 'Created_tmp_disk_tables';
SHOW STATUS LIKE 'Created_tmp_tables';
-- 比率 > 10% 说明频繁溢出
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size';
修复:调大 tmp_table_size 和 max_heap_table_size(两者取较小值);优化 SQL 减少临时表;tmpdir 使用 SSD。
故障 10:大表 DDL 阻塞业务
现象:执行 ALTER TABLE 后该表所有查询阻塞(Waiting for table metadata lock)
修复:使用在线 DDL 工具:
pt-online-schema-change --execute \
--alter "ADD COLUMN status TINYINT DEFAULT 0" \
D=mydb,t=mytable --host=127.0.0.1
# 或 gh-ost
gh-ost --host=127.0.0.1 --database=mydb --table=mytable \
--alter="ADD COLUMN status TINYINT DEFAULT 0" --execute
⚠️
pt-osc和gh-ost都会产生额外 IO 和主从延迟,高峰期需设节流参数。
故障 11:字符集不一致导致索引失效
现象:JOIN 条件明明有索引但不走,EXPLAIN 显示全表扫描
原因:两表字段字符集不同(如 utf8mb4 vs utf8),JOIN 时发生隐式转换 → 索引失效。
修复:统一字段字符集:
ALTER TABLE table_b MODIFY COLUMN name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
故障 12:慢日志未轮转占满磁盘
排查:
ls -lh $(mysql -N -e "SHOW VARIABLES LIKE 'slow_query_log_file'" | awk '{print $2}')
修复:配置 logrotate 定期轮转。
第二部分:Redis 常见故障
故障 13:内存耗尽(OOM)
现象:写入报 OOM command not allowed when used memory > 'maxmemory'
排查:
redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli INFO stats | grep evicted_keys
修复:紧急调大 maxmemory;调整 maxmemory-policy(推荐 allkeys-lru);扫描并拆分/删除大 key;长期扩容或优化数据模型。
故障 14:大 Key 导致阻塞
现象:Redis 响应间歇性卡顿,CPU 出现尖峰
排查:
redis-cli --bigkeys # 按 key 数量维度
redis-cli --memkeys # 按内存维度
redis-cli MEMORY USAGE <key> # 单个 key 内存
修复:拆分大 Hash/List/Set(按 hash tag 分片);用 UNLINK 替代 DEL(异步删除,Redis 4.0+);业务侧避免往单个 key 无限追加。
故障 15:慢查询
现象:SLOWLOG 中有异常命令
排查:
redis-cli SLOWLOG GET 20
redis-cli SLOWLOG LEN
常见慢命令:KEYS *(应替换为 SCAN)、HGETALL 大 Hash(应改为 HSCAN)、DEL 大 key(改为 UNLINK)、SORT 大集合。
故障 16:主从复制延迟/中断
现象:INFO replication 显示 master_link_status:down 或 offset 持续落后
排查:
redis-cli INFO replication
# 关注:role / connected_slaves / master_link_status / master_repl_offset
根因:主库写入量超过从库处理能力;网络带宽不足;从库正在做 RDB 持久化阻塞复制。
修复:在从库关闭 AOF/RDB(仅在从库做持久化由主库负责);调大 repl-backlog-size;使用 repl-diskless-sync(无盘复制)。
故障 17:Sentinel 频繁切换
现象:主从频繁切换,监控不断告警
排查:
redis-cli -p 26379 SENTINEL MASTERS
redis-cli -p 26379 SENTINEL SENTINELS mymaster
# 检查 sentinel.conf 中 down-after-milliseconds 是否太敏感
修复:调大 down-after-milliseconds(建议 30000-60000ms);增加 Sentinel 实例数(≥3 个,奇数);检查网络是否存在间歇性丢包。
故障 18:内存碎片率高
现象:used_memory_rss / used_memory > 1.5
排查:
redis-cli INFO memory | grep mem_fragmentation_ratio
修复:Redis 4.0+ 开启 activedefrag yes;极端情况重启实例(注意持久化完整性)。
故障 19:RDB/AOF 持久化阻塞
现象:fork 期间 Redis 响应卡顿或暂时无响应
排查:
redis-cli INFO stats | grep latest_fork_usec
# fork 耗时 > 1 秒即需关注
redis-cli INFO persistence | grep rdb_bgsave_in_progress
修复:控制单个 Redis 实例内存 < 10GB(降低 fork 开销);非必要不在从库做 RDB;AOF 使用 everysec 策略。
故障 20:连接数耗尽
现象:客户端报 ERR max number of clients reached
排查:
redis-cli INFO clients | grep -E 'connected_clients|maxclients'
redis-cli CLIENT LIST | awk '{print $2}' | sort | uniq -c | sort -rn | head
# 看哪些 IP 连接最多
修复:调大 maxclients;设置 timeout 清理空闲连接(CONFIG SET timeout 300);修复客户端连接泄漏。
故障 21:缓存雪崩
现象:大量缓存同时过期,请求全部打到数据库
修复:设置 TTL 时加随机偏移:
import random
base_ttl = 3600 # 1小时
jitter = random.randint(0, 600)
redis.setex(key, base_ttl + jitter, value)
多级缓存(本地缓存 + Redis);热点 key 不设过期 + 后台定期刷新;限流/熔断保护后端。
故障 22:缓存穿透
现象:大量查询不存在的数据,每次都打到数据库
修复:
# 方案一:缓存空值(短 TTL)
redis-cli SETEX "user:99999" 60 "NULL"
# 方案二:布隆过滤器(RedisBloom)
redis-cli BF.ADD exists_filter "user:1"
redis-cli BF.EXISTS exists_filter "user:99999"
# 返回 0 表示一定不存在,不查数据库
故障 23:缓存击穿
现象:某个热点 key 过期瞬间,大量请求同时打到数据库
修复——互斥锁模式:
def get_with_lock(key):
value = r.get(key)
if value:
return value
lock_key = f"lock:{key}"
if r.set(lock_key, "1", nx=True, ex=10):
try:
value = query_from_db(key)
r.setex(key, 3600, value)
finally:
r.delete(lock_key)
return value
else:
time.sleep(0.1)
return r.get(key)
或使用"逻辑过期"——缓存不设物理 TTL,value 中含逻辑过期时间,后台异步刷新。
故障排查决策树
MySQL
MySQL 异常
├── 连不上
│ ├── Too many connections → 故障 1
│ ├── Access denied → 权限问题
│ └── Can't connect → 网络/服务未启动
├── 查询慢
│ ├── 单条 SQL 慢 → 故障 3(慢查询)
│ ├── 整体变慢 → 故障 8(Buffer Pool)/ 故障 9(临时表溢出)
│ └── 间歇性慢 → 故障 4(死锁)/ 故障 5(MDL)
├── 数据不一致
│ ├── 主从延迟 → 故障 2
│ └── 复制中断 → 故障 7
└── 磁盘满 → 故障 6
Redis
Redis 异常
├── 连不上
│ ├── max clients reached → 故障 20
│ └── Connection refused → 服务未运行
├── 响应慢
│ ├── 单命令慢 → 故障 15(慢查询)
│ ├── 整体慢 → 故障 14(大 key)
│ └── 间歇性慢 → fork/AOF rewrite
├── 写入失败
│ └── OOM → 故障 13
├── 数据丢失
│ ├── 淘汰 → 故障 13
│ └── 穿透/雪崩/击穿 → 故障 21/22/23
└── 主从问题
├── 同步延迟/中断 → 故障 16
└── 频繁切换 → 故障 17
常用命令速查
MySQL
-- 连接排查
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Threads%';
SELECT * FROM sys.session;
-- 锁排查
SHOW ENGINE INNODB STATUS\G
SELECT * FROM sys.innodb_lock_waits;
SELECT * FROM information_schema.innodb_trx;
-- 复制排查
SHOW REPLICA STATUS\G -- MySQL 8.0
SHOW SLAVE STATUS\G -- MySQL 5.7
-- 性能排查
SHOW STATUS LIKE 'Innodb_buffer_pool%';
SHOW STATUS LIKE 'Created_tmp%';
Redis
redis-cli INFO # 全局状态
redis-cli INFO memory # 内存详情
redis-cli INFO replication # 复制状态
redis-cli SLOWLOG GET 20 # 慢查询
redis-cli CLIENT LIST # 连接列表
redis-cli --bigkeys # 大 key 扫描
redis-cli CLUSTER INFO # 集群状态
生产环境注意事项
| 原则 | 说明 |
|---|---|
| 不在主库执行长查询 | 分析类查询走从库 |
| 备份策略 | MySQL:全量备份 + 连续 binlog;Redis:根据容忍度选 RDB/AOF/混合 |
| 参数修改走流程 | 测试验证 → 变更单 → 低峰执行 → 观察 10-30 分钟 |
| 监控先行 | 操作前确认监控正常,操作后持续观察 |
| 操作有记录 | 所有操作记录到运维日志或工单 |
| 主从切换有预案 | 切换前确认从库追平,切换后检查应用连接 |
| Redis 不做唯一存储 | 底层有持久化数据源兜底 |
| 每周巡检 | max_connections 使用率、Buffer Pool 命中率、主从延迟、慢查询数、Redis 内存使用率、key 淘汰数 |
修复后验证
MySQL:Threads_connected 回正常水位;Seconds_Behind_Master 趋于 0;SHOW ENGINE INNODB STATUS 无新增死锁;监控曲线(QPS/延迟/连接数)恢复基线。
Redis:used_memory 在预期范围;master_link_status: up;keyspace_hits 比例恢复;SLOWLOG LEN 不再增长。
回滚方案
MySQL 参数修改回滚:SET GLOBAL xxx = <原始值>;(修改了 my.cnf 需重启)。
Redis:CONFIG SET 修改是临时的,重启恢复原始值(用了 CONFIG REWRITE 需恢复 redis.conf 备份)。
主从复制修复回滚——最终方案是重建从库(xtrabackup 全量备份 → 从库恢复 → 重新配置复制)。
关联页面
| 页面 | 关联点 |
|---|---|
| mysql-performance-config | MySQL 性能调优深度指南(慢查询/锁/死锁/索引失效/深度分页/Buffer Pool) |
| redis-memory-optimization | Redis 内存优化完全指南(数据结构/TTL/淘汰策略/Bigkey/碎片治理) |
| redis-connection-management | Redis 连接管理与配置优化 |
| mysql-replication-guide | MySQL 主从复制完整指南 |
| mysql-slow-query-case-study | MySQL 慢查询排查案例复盘(真实线上案例) |
| redis-ha-replication-sentinel | Redis 高可用:主从复制 + Sentinel 哨兵模式 |
| redis-persistence-strategy | Redis 持久化策略(RDB/AOF/混合) |
| fullstack-performance-troubleshooting | 全栈性能排障入口(MySQL/Redis 在整体链路中的定位) |