mysql长事务有什么危害_mysql性能影响分析

来源:这里教程网 时间:2026-02-28 20:48:20 作者:

长事务会阻塞DDL操作

MySQL在执行

ALTER TABLE
DROP INDEX
等DDL语句时,需要获取表级元数据锁(MDL write lock),而活跃的长事务会一直持有该表的MDL read lock,导致DDL被挂起,直到长事务提交或回滚。现象是
SHOW PROCESSLIST
中出现大量
Waiting for table metadata lock
状态。

即使长事务只执行了
SELECT
(显式开启事务后未提交),也会持续持有MDL读锁
5.7+ 版本中,
innodb_lock_wait_timeout
不影响MDL等待,DDL可能无限期阻塞
线上紧急加索引或清理表时,常因一个未提交的
BEGIN; SELECT ...;
卡住整个变更流程

长事务拖慢InnoDB purge线程

InnoDB通过purge线程异步清理undo日志中的过期版本。长事务的事务ID(trx_id)会成为全局最小活跃事务ID(min_trx_id),导致其开始后产生的所有undo记录都无法被purge。结果是:

information_schema.INNODB_TRX
TRX_ROWS_MODIFIED
可能不大,但
TRX_UNDO_BYTES
持续增长
SHOW ENGINE INNODB STATUS
HISTORY LIST
长度飙升(如 > 100万),严重时达数千万
磁盘空间占用增加,undo表空间膨胀;同时purge延迟加剧,MVCC快照变“重”,
SELECT
扫描更多旧版本行

长事务放大锁冲突与死锁概率

事务越长,持有行锁、间隙锁的时间就越久,与其他并发事务的重叠窗口越大。尤其在高并发更新场景下:

一个长事务执行
UPDATE t SET x=1 WHERE id=100;
后未提交,后续所有对
id=100
的DML都会被阻塞
若另一事务试图
INSERT INTO t VALUES (101, ...)
,且
id
是主键,InnoDB需加插入意向锁,与长事务持有的间隙锁冲突,直接触发死锁
应用层重试逻辑若未处理
Deadlock found when trying to get lock
,可能形成雪崩式重试

如何快速定位和限制长事务

核心思路是监控 + 主动拦截。不要依赖事后排查。

定期查
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 60;
,重点关注
TRX_STATE = 'RUNNING'
TRX_COMMAND = 'Sleep'
的连接(通常是应用未关闭事务)
设置
wait_timeout
interactive_timeout
(如300秒),但注意:这仅对空闲连接生效,对已执行SQL但未提交的事务无效
更有效的是在应用侧统一使用带超时的事务模板,例如Java里用
@Transactional(timeout = 30)
;或在MySQL 5.7+ 中启用
innodb_rollback_on_timeout=ON
(注意:它只对锁等待超时生效,不适用于普通长事务)
关键业务库可配置
max_execution_time=30000
(单位毫秒),配合
SET SESSION MAX_EXECUTION_TIME=30000;
限制单条SQL运行时长,间接抑制长事务内SQL失控
SELECT 
  ID,
  USER,
  HOST,
  DB,
  COMMAND,
  TIME,
  STATE,
  INFO
FROM information_schema.PROCESSLIST 
WHERE TIME > 60 AND COMMAND != 'Sleep';

真正棘手的不是“怎么查”,而是那些不走标准ORM、手动BEGIN/COMMIT又忘记异常兜底的脚本——它们往往藏在定时任务或运维工具里,得靠审计日志和业务梳理才能揪出来。

相关推荐