引言
在 mysql 日常运维、业务开发过程中,经常会遇到接口超时、sql 执行卡死、数据库锁等待、事务阻塞的问题。大多场景都是因为长事务、未提交事务、行锁竞争导致的事务阻塞。
本文给大家分享一套从零排查阻塞事务、定位阻塞sql、精准kill问题线程的完整实战流程,语句可直接复制复用,快速解决mysql锁等待、事务卡死问题。
一、问题场景概述
当出现以下现象时,基本可以判定是mysql事务阻塞导致:
- 单条update/delete/insert语句长时间执行不结束
- 业务接口请求超时、响应缓慢
- 数据库cpu、内存无异常,但qps骤降
- 频繁出现 lock wait timeout exceeded 锁等待超时报错
核心原因:一个事务持有行锁未提交,导致后续请求的事务阻塞等待,形成锁等待链路。
二、完整排查解决流程
整套流程分为4个步骤:查询阻塞关系 → 根据线程pid查内核线程id → 定位阻塞源头sql → 杀死阻塞线程恢复服务。
1. 查看所有被阻塞的事务与阻塞源头
通过 `innodb_lock_waits` 和 `innodb_trx` 系统表,直接查询出等待事务和源头阻塞事务的对应关系,是排查锁阻塞的核心语句。
-- 查看事务阻塞关系:等待事务、阻塞事务、对应sql、线程id
select
r.trx_id as waiting_trx_id, -- 被阻塞的事务id
r.trx_mysql_thread_id as waiting_thread, -- 被阻塞的业务线程pid
r.trx_query as waiting_query, -- 被阻塞的sql语句
b.trx_id as blocking_trx_id, -- 阻塞别人的源头事务id
b.trx_mysql_thread_id as blocking_thread, -- 阻塞源头线程pid
b.trx_query as blocking_query -- 造成阻塞的源头sql
from information_schema.innodb_lock_waits w
inner join information_schema.innodb_trx b
on b.trx_id = w.blocking_trx_id
inner join information_schema.innodb_trx r
on r.trx_id = w.requesting_trx_id;
字段说明
- waiting_trx/waiting_thread/waiting_query:正在等待锁、被卡住的事务、线程、sql
- blocking_trx/blocking_thread/blocking_query:持有锁、导致别人阻塞的源头事务、线程、sql
通过该语句可以直接定位到:到底是哪一条sql、哪一个线程卡住了整个业务。
2. 根据业务线程pid查询内核线程id
通过第一步查询到的 `blocking_thread`(processlist_id),查询对应的数据库内核线程 thread_id,用于精准定位线程详情。
-- 根据业务线程pid查询内核线程id(替换为自己查询到的pid) select thread_id from performance_schema.threads where processlist_id = 10146;
参数说明:将 `10146` 替换为第一步查到的 blocking_thread 阻塞线程id。
3. 查询阻塞线程正在执行的完整sql
通过上一步获取的 thread_id,查询线程当前正在执行的真实sql,确认阻塞源头业务语句。
-- 根据内核线程id,查询当前执行的sql语句 select thread_id, sql_text from performance_schema.events_statements_current where thread_id = 10146;
该语句可以精准拿到造成锁阻塞的原始sql,方便后续复盘优化(比如未加索引、长事务、事务未提交等问题)。
4. 杀死阻塞线程,恢复业务
确认阻塞线程和问题sql后,直接kill掉阻塞源头线程,释放锁,恢复正常业务访问。
-- 杀死阻塞线程(替换为第一步查询到的 blocking_thread 值) kill xxx;
注意:kill 阻塞线程(blocking_thread),不要kill被阻塞的线程,kill源头才能彻底释放锁。
三、核心原理简单说明
- information_schema.innodb_trx:记录当前innodb所有运行中的事务信息,包含事务id、线程id、执行sql、事务状态
- information_schema.innodb_lock_waits:专门记录锁等待关系,是阻塞排查的核心视图
- performance_schema:性能监控库,可精准查询线程对应的真实执行sql
四、常见阻塞原因与优化方案
1. 事务执行时间过长(长事务)
业务代码中事务开启后,执行大量业务逻辑、网络请求,迟迟不commit,长期持有行锁。
优化:缩小事务范围,事务内只做数据库操作,避免事务嵌套业务逻辑。
2. 更新/删除语句无索引
where条件无索引,导致行锁升级为表锁,阻塞全表所有读写操作。
优化:保证update/delete语句where字段建立索引。
3. 程序异常导致事务未提交
代码报错、接口中断,导致事务未执行commit/rollback,线程挂起持有锁。
优化:全局异常捕获,确保事务最终一定会提交或回滚。
五、排查总结(速查清单)
- 执行阻塞查询sql,找到 blocking_thread 阻塞源头线程
- 通过pid查询内核线程id,核对阻塞sql
- 确认无误后 kill 阻塞线程,快速恢复业务
- 根据阻塞sql复盘代码,解决长事务、无索引、事务未提交问题
六、补充:日常预防建议
- 禁止大事务、长事务,单事务执行时间控制在毫秒/秒级
- 所有更新语句必须走索引,避免行锁变表锁
- 开启mysql慢查询日志,监控超时sql
- 定时巡检数据库长事务,提前规避阻塞问题
以上就是mysql事务阻塞卡死问题完整排查与解决流程的详细内容,更多关于mysql事务阻塞卡死问题的资料请关注代码网其它相关文章!
发表评论