事务粒度太大,导致锁等待和回滚开销高
长事务会持有锁时间过长,阻塞其他事务,同时 undo log 持续增长,影响 purge 线程效率。常见于把整个批量导入、报表生成或跨服务操作包在一个事务里。
拆分大事务:比如INSERT INTO t VALUES (...)10 万行,改用每 1000 行提交一次,用循环控制
COMMIT避免在事务中调用外部 HTTP 接口或文件读写,这些不可控延迟会直接拖长事务生命周期 确认是否真需要事务:只读查询、单行
UPDATE且无并发更新冲突场景,可考虑去掉
BEGIN/COMMIT
隔离级别设为 REPEATABLE READ
却没用到 MVCC 优势
MySQL 默认的
REPEATABLE READ在多数只读+少量更新场景下反而增加 gap lock 开销,尤其配合
SELECT ... FOR UPDATE时容易引发死锁。 读多写少业务(如内容展示、后台查询)可降级为
READ COMMITTED,减少锁范围,提升并发 若必须用
REPEATABLE READ,确保 WHERE 条件走索引,否则会升级为表级锁或全范围 gap lock 检查
innodb_lock_wait_timeout是否过短(默认 50 秒),超时后应用层重试逻辑不完善会导致雪崩式重试
频繁短事务 + 高并发触发 log_flush
瓶颈
每提交一次事务,InnoDB 默认刷一次 redolog(
innodb_flush_log_at_trx_commit = 1),磁盘 I/O 成为瓶颈,尤其在 SSD 性能一般或日志盘负载高的机器上。 允许部分数据丢失风险时,可设
innodb_flush_log_at_trx_commit = 2(每秒刷一次 log),吞吐量通常提升 3–5 倍 若使用 RAID 或企业级 NVMe,且业务对 crash 安全性要求极高,保持
=1,但需确认
innodb_log_file_size足够(建议 ≥ 512MB),避免频繁 checkpoint 禁用
autocommit=0后手动控制事务边界,但忘记
COMMIT会导致连接长期占用,务必配超时监控
未清理历史事务或长事务未被发现
长时间未提交的事务(如应用崩溃未 rollback、调试时中断连接)会卡住 purge 线程,导致
trx_rseg_history_len持续上涨,最终拖慢所有 DML。 定期查
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60,找出运行超 1 分钟的事务 启用
innodb_print_all_deadlocks = ON,结合 error log 分析死锁根因,而不是只看应用层报错 应用层加事务模板:用
SET SESSION innodb_lock_wait_timeout = 10限制单次锁等待,避免一个慢事务拖垮整条链路
事务优化不是调几个参数就能一劳永逸的事,真正难的是在业务逻辑里识别出“哪里真的需要事务”“哪里其实可以妥协”。很多性能问题根源不在 MySQL,而在代码里一个没关的
Connection或一个没设 timeout 的 RPC 调用。
