线上MySQL数据库卡死了!所有业务都不可用!

线上MySQL数据库卡死了!所有业务都不可用!

【MySQL死锁终结者】5分钟彻底解决数据库"卡死"难题! - 知乎凌晨2点,手机铃声突然响起——"MySQL数据库卡死了!所有业务都不可用!"这是每个运维工程师最不想接到的电话。生产环境的MySQL突然卡死,不仅会造成业务中断,还可能导致数据丢失和客户投诉。经历过多次生产事故后,我总结出了一套行之有效的5步排查法,帮助我在最短时间内定位并解决问题。本文将详细分享这套实战方法,让你在遇到类似问题时能够快速响应、准确定位。

技术背景:MySQL"卡死"的本质什么是MySQL卡死?MySQL"卡死"并不是一个准确的技术术语,它通常指数据库响应极其缓慢或完全无响应的状态。从技术角度来看,这种现象可能由多种原因引起:

• 锁等待:表锁、行锁或元数据锁导致大量线程阻塞• 慢查询堆积:复杂SQL消耗大量资源,导致连接池耗尽• 磁盘I/O瓶颈:磁盘满了、I/O性能不足或存储故障• 内存耗尽:Buffer Pool、临时表空间等内存资源不足• 连接数打满:max_connections达到上限,新连接无法建立MySQL的进程状态机制理解MySQL的线程状态对于排查问题至关重要。每个客户端连接在MySQL内部对应一个线程,这些线程可能处于不同的状态:

• Sleep:等待客户端发送新请求• Query:正在执行查询• Locked:等待表锁• Waiting for table metadata lock:等待元数据锁• Sending data:正在处理SELECT语句并发送数据• Sorting result:正在对结果进行排序• Creating tmp table:正在创建临时表当大量线程长时间处于某个非正常状态时,就会表现为"卡死"。

历史教训:一次真实的生产事故2023年双11期间,我们的电商系统在流量高峰期突然出现MySQL卡死。监控显示连接数在10秒内从200飙升到2000(上限),所有API请求超时。最终排查发现是一个未加索引的关联查询在大表上执行,导致表锁持续时间过长,引发雪崩效应。这次事故让我深刻认识到系统性排查方法的重要性。

核心内容:5步排查法实战详解第一步:快速确认数据库进程状态目标:在30秒内确认MySQL进程是否还活着,基本资源消耗如何。

操作命令代码语言:javascript复制# 检查MySQL进程状态

ps aux | grep mysqld

# 查看MySQL进程的资源占用

top -p $(pidof mysqld)

# 快速查看系统负载

uptime

# 检查磁盘空间

df -h

关键判断指标1. 进程是否存在:如果进程不存在,说明MySQL已崩溃,需要检查错误日志并重启2. CPU占用率:• 接近100%:可能是慢查询或全表扫描• 很低(<10%):可能是锁等待或I/O瓶颈3. 内存占用:是否接近物理内存上限,是否发生OOM4. 磁盘空间:数据盘或系统盘是否已满实战案例某次故障中,我发现MySQL进程CPU占用率只有5%,但load average高达50。这个反常现象提示问题不在CPU计算上,而是大量线程在等待某种资源(后来证实是表锁)。

第二步:检查当前连接和线程状态目标:找出正在执行的SQL和阻塞情况。

核心命令代码语言:javascript复制-- 查看当前所有连接

SHOWFULL PROCESSLIST;

-- 查看当前运行时间超过2秒的线程

SELECT*FROM information_schema.PROCESSLIST

WHERE COMMAND !='Sleep'ANDTIME>2

ORDERBYTIMEDESC;

-- 统计各状态的线程数量

SELECT STATE, COUNT(*) as count

FROM information_schema.PROCESSLIST

GROUPBY STATE

ORDERBY count DESC;

关键信息解读SHOW PROCESSLIST关键字段:

• Id:线程ID,可用于KILL命令• User:连接用户• Host:客户端IP• db:当前数据库• Command:当前执行的命令类型(Query/Sleep等)• Time:当前状态持续时间(秒)• State:线程状态• Info:正在执行的SQL语句危险信号:

• 大量线程处于Locked或Waiting for table metadata lock状态• 单个查询Time超过60秒仍在执行• Info字段显示明显的慢查询或全表扫描• Sleep状态连接数接近max_connections实战技巧我习惯在终端开两个窗口,一个持续执行SHOW PROCESSLIST,另一个用于分析和执行KILL操作。这样可以实时观察线程状态变化,快速判断处理效果。

代码语言:javascript复制# 在一个窗口持续监控

watch -n 2 'mysql -e "SHOW PROCESSLIST\G" | grep -E "(Id|User|State|Time|Info)"'

第三步:定位锁等待和死锁目标:识别锁冲突,找到持有锁的源头线程。

InnoDB锁查询代码语言:javascript复制-- 查看当前锁等待情况(MySQL 5.7+)

SELECT

r.trx_id AS waiting_trx_id,

r.trx_mysql_thread_id AS waiting_thread,

r.trx_query AS waiting_query,

b.trx_id AS blocking_trx_id,

b.trx_mysql_thread_id AS blocking_thread,

b.trx_query AS blocking_query

FROM information_schema.innodb_lock_waits w

INNERJOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id

INNERJOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;

-- MySQL 8.0+ 新版本使用

SELECT*FROM performance_schema.data_locks;

SELECT*FROM performance_schema.data_lock_waits;

-- 查看死锁日志

SHOW ENGINE INNODB STATUS\G

元数据锁(MDL)排查元数据锁常被忽视,但它是导致"卡死"的常见原因,尤其是在执行DDL操作时。

代码语言:javascript复制-- 查看元数据锁等待(MySQL 5.7+)

SELECT

locked_table_schema,

locked_table_name,

locked_type,

waiting_processlist_id,

waiting_query,

blocking_processlist_id

FROM sys.schema_table_lock_waits;

典型场景分析场景1:未提交事务持有锁

某次故障中,一个开发人员在测试环境执行了BEGIN; UPDATE users SET status=1;后忘记提交,连接保持在Sleep状态。随后的业务更新操作全部被阻塞。

解决方法:

代码语言:javascript复制-- 找到Sleep状态但有未提交事务的连接

SELECT*FROM information_schema.innodb_trx WHERE trx_state ='RUNNING';

KILL ;

场景2:大表DDL引发元数据锁

在线上执行ALTER TABLE时,如果有长时间运行的查询未结束,会导致DDL被阻塞,进而阻塞所有后续的该表访问。

预防措施:

• 使用pt-online-schema-change等工具• 设置lock_wait_timeout限制等待时间• 在业务低峰期执行DDL第四步:分析慢查询和系统瓶颈目标:识别资源消耗型查询和系统瓶颈。

慢查询日志分析代码语言:javascript复制# 使用pt-query-digest分析慢查询日志

pt-query-digest /var/log/mysql/slow.log | head -n 100

# 或使用mysqldumpslow

mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

实时性能诊断代码语言:javascript复制-- 查看当前系统资源使用

SHOWGLOBAL STATUS LIKE'%thread%';

SHOWGLOBAL STATUS LIKE'%connection%';

-- 查看InnoDB状态

SHOW ENGINE INNODB STATUS\G

-- 重点关注以下指标:

-- 1. Threads_connected vs max_connections

-- 2. Threads_running (正在执行的线程数,超过CPU核心数2倍需警惕)

-- 3. Innodb_row_lock_waits (行锁等待次数)

-- 4. Innodb_buffer_pool_wait_free (等待空闲页的次数)

系统层面检查代码语言:javascript复制# I/O性能检查

iostat -x 1 10

# 关注指标:

# - %util 接近100%表示I/O饱和

# - await 平均等待时间,SSD应<5ms,HDD应<20ms

# 查看MySQL相关的I/O情况

iotop -p $(pidof mysqld)

# 内存检查

free -h

cat /proc/$(pidof mysqld)/status | grep -E '(VmSize|VmRSS)'

实战经验曾遇到一个案例,processlist显示大量Sending data状态,但单个查询并不慢。最终发现是网络带宽打满——业务同时导出大量数据,导致网络成为瓶颈。这提醒我们排查要全面,不能只盯着数据库本身。

第五步:应急处理和恢复目标:快速恢复业务,避免二次伤害。

紧急处理措施1. KILL阻塞线程

代码语言:javascript复制-- 批量KILL某个用户的连接

SELECT CONCAT('KILL ',id,';') FROM information_schema.PROCESSLIST

WHEREUSER='webapp'ANDTIME>60;

-- 复制输出的KILL语句执行

-- 或使用存储过程批量KILL(谨慎使用)

DELIMITER $$

CREATEPROCEDURE kill_long_queries()

BEGIN

DECLARE done INTDEFAULTFALSE;

DECLARE thread_id INT;

DECLARE cur CURSORFOR

SELECT id FROM information_schema.PROCESSLIST

WHERE COMMAND !='Sleep'ANDTIME>120;

DECLARE CONTINUE HANDLER FORNOT FOUND SET done =TRUE;

OPEN cur;

read_loop: LOOP

FETCH cur INTO thread_id;

IF done THEN

LEAVE read_loop;

END IF;

KILL thread_id;

END LOOP;

CLOSE cur;

END$$

DELIMITER ;

CALL kill_long_queries();

2. 临时调整参数

代码语言:javascript复制-- 临时增加最大连接数(治标不治本)

SETGLOBAL max_connections =2000;

-- 降低锁等待超时时间

SETGLOBAL innodb_lock_wait_timeout =10;

-- 增加连接超时时间,避免频繁重连

SETGLOBAL wait_timeout =600;

3. 重启MySQL(最后手段)

代码语言:javascript复制# 平滑重启

systemctl restart mysql

# 如果无法停止,强制终止

killall -9 mysqld

systemctl start mysql

⚠️ 重启风险:

• 未提交事务会丢失• 业务需要重新建立连接• 大内存实例启动可能需要较长时间恢复后的验证代码语言:javascript复制-- 检查数据库状态

SHOWGLOBAL STATUS;

SHOW VARIABLES;

-- 检查主从复制状态(如果有)

SHOW SLAVE STATUS\G

-- 验证业务关键表

SELECTCOUNT(*) FROM critical_business_table;

-- 检查错误日志

tail -f /var/log/mysql/error.log

故障复盘清单恢复业务后,必须进行详细复盘:

1. 记录故障时间线和关键截图2. 保存processlist、innodb status等诊断信息3. 导出相关时间段的慢查询日志4. 分析根本原因(RC Root Cause)5. 制定预防措施和优化计划6. 更新监控和告警规则实践案例:真实故障的完整处理过程案例背景某电商平台在促销活动期间,MySQL数据库突然出现大面积卡死,订单系统、用户中心等核心服务全部不可用,影响用户数超过10万。

故障时间线14:23 - 监控告警:MySQL连接数异常,API响应时间超过10s

14:24 - 运维团队接到告警,开始排查

14:25 - 执行第一步:确认MySQL进程正常,CPU 15%,load 80

14:26 - 执行第二步:发现1800+个连接,大量Waiting for table metadata lock

14:27 - 执行第三步:定位到一个执行了12分钟的ALTER TABLE操作

14:28 - 找到阻塞源头:一个未提交的SELECT查询(开启了事务)

14:29 - KILL阻塞查询和DDL操作

14:30 - 连接数开始下降,业务逐步恢复

14:35 - 所有服务恢复正常

根因分析1. 直接原因:开发人员在生产环境测试功能时,开启事务执行SELECT后未提交,连接保持2. 诱发因素:DBA在此期间执行表结构变更,被未提交事务阻塞3. 扩散原因:后续所有访问该表的请求都被MDL阻塞,连接数迅速打满处理要点• ✅ 快速定位:利用5步法在7分钟内找到根本原因• ✅ 果断处理:直接KILL问题连接,不纠结于保留"测试数据"• ✅ 二次确认:恢复后检查数据一致性和主从同步状态改进措施1. 流程规范:生产环境禁止手动开启事务测试,必须使用只读账户2. 技术防护:• 设置autocommit=1为默认值• 开启innodb_lock_wait_timeout=10• 配置max_execution_time限制查询时间3. 监控增强:• 新增元数据锁监控告警• 监控未提交事务超过5分钟的连接4. 应急预案:编写自动化脚本,在检测到锁等待超过阈值时自动处理成本收益• 故障影响时间:12分钟• 业务损失:约15万元(订单转化率下降估算)• 改进投入:2人日• 预防效果:后续3个月内未再发生类似问题最佳实践与预防措施配置优化建议代码语言:javascript复制# my.cnf 关键配置

[mysqld]

# 连接管理

max_connections = 2000

max_connect_errors = 10000

wait_timeout = 600

interactive_timeout = 600

# 锁相关

innodb_lock_wait_timeout = 10

lock_wait_timeout = 5

table_open_cache = 4000

# 慢查询监控

slow_query_log = 1

long_query_time = 1

log_queries_not_using_indexes = 1

# 性能优化

innodb_buffer_pool_size = 物理内存的70%

innodb_log_file_size = 1G

innodb_flush_log_at_trx_commit = 2# 非金融业务可设为2

监控告警体系关键指标:

1. 连接数监控:• 阈值:当前连接数 > max_connections * 0.7• 频率:每30秒检查一次2. 长时间运行查询:• 阈值:运行时间 > 60秒• 动作:自动记录并告警3. 锁等待监控:• 阈值:等待时间 > 5秒• 动作:记录等待链并告警4. 线程堆积:• 阈值:Threads_running > CPU核心数 * 2• 动作:告警并分析processlist5. 磁盘空间:• 阈值:数据盘使用率 > 80%• 频率:每小时检查日常维护清单每日:

• 检查慢查询日志,优化TOP10慢查询• 查看error log,关注异常重启和警告信息• 确认备份任务正常完成每周:

• 分析表增长趋势,规划容量• 检查碎片率高的表,执行OPTIMIZE TABLE• 审计新增SQL,确保有正确索引每月:

• 全面性能测试,压力测试• 更新应急预案和runbook• 复盘过去一个月的所有故障团队能力建设1. 编写Runbook:标准化故障处理流程,新人也能快速响应2. 定期演练:每季度进行一次故障模拟演练3. 知识沉淀:每次故障必须输出复盘文档并分享4. 工具建设:开发自动化诊断脚本,减少人工判断时间总结与展望MySQL"卡死"是运维工作中最常见也最紧急的故障之一。通过本文介绍的5步排查法——确认进程状态、检查连接线程、定位锁等待、分析系统瓶颈、应急恢复处理——可以在大多数情况下快速定位问题并恢复业务。

核心要点回顾1. 快速响应:故障发生后的前5分钟最关键,准确判断比盲目操作更重要2. 系统思维:MySQL性能问题往往是多因素综合作用,要从数据库、系统、网络、应用等多个层面分析3. 预防为主:通过规范的配置、完善的监控和定期的优化,可以避免90%的"卡死"问题4. 持续改进:每次故障都是改进系统的机会,复盘和预防措施必不可少技术发展趋势随着云原生和分布式数据库的发展,传统MySQL的一些痛点正在被新技术解决:

• 云数据库RDS:提供自动化的监控、备份和故障切换• 读写分离:通过代理层(如ProxySQL)实现查询分流• 分布式数据库:TiDB、OceanBase等提供更好的扩展性• 智能诊断:基于AI的自动化故障诊断和自愈系统但无论技术如何演进,深入理解数据库工作原理、掌握系统化的排查方法,始终是每个运维工程师的核心能力。希望这篇文章能帮助你在遇到MySQL"卡死"时,不再慌乱,而是有条不紊地快速解决问题。

记住:故障不可怕,可怕的是没有应对方法。

快捷导航网站 https://www.kjdaohang.com,近期发布大量AI实用工具、免费学习教程,欢迎PC端用户体验使用!

👇 点击下方关注公众号《测试开发技术》,获取免费测开学习路线、简历模板、面试真题、AI测试、AI 编程、自动化测试、测试开发资料教程等。

相关文章

python中变量的基本使用及命名规则
beat365最新版体育

python中变量的基本使用及命名规则

🕒 02-13 👁️ 6366
4.2吨等于多少千克?
beat365最新版体育

4.2吨等于多少千克?

🕒 09-18 👁️ 3264
windows hello指纹设置不了解决方法教程【详解】
beat365官方入口素描网

windows hello指纹设置不了解决方法教程【详解】

🕒 07-02 👁️ 6384